androjack-mcp 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
- package/.github/pull_request_template.md +16 -0
- package/CONTRIBUTING.md +27 -0
- package/LICENSE +21 -0
- package/README.md +592 -0
- package/SECURITY.md +26 -0
- package/assets/AndroJack banner.png +0 -0
- package/assets/killer_argument.png +0 -0
- package/build/constants.js +412 -0
- package/build/http-server.js +163 -0
- package/build/http.js +151 -0
- package/build/index.js +553 -0
- package/build/install.js +379 -0
- package/build/logger.js +57 -0
- package/build/tools/api-level.js +170 -0
- package/build/tools/api36-compliance.js +282 -0
- package/build/tools/architecture.js +75 -0
- package/build/tools/build-publish.js +362 -0
- package/build/tools/component.js +90 -0
- package/build/tools/debugger.js +82 -0
- package/build/tools/gradle.js +234 -0
- package/build/tools/kmp.js +348 -0
- package/build/tools/kotlin-patterns.js +500 -0
- package/build/tools/large-screen.js +366 -0
- package/build/tools/m3-expressive.js +447 -0
- package/build/tools/navigation3.js +331 -0
- package/build/tools/ondevice-ai.js +283 -0
- package/build/tools/permissions.js +404 -0
- package/build/tools/play-policy.js +221 -0
- package/build/tools/scalability.js +621 -0
- package/build/tools/search.js +89 -0
- package/build/tools/testing.js +439 -0
- package/build/tools/wear.js +337 -0
- package/build/tools/xr.js +274 -0
- package/config/antigravity_mcp.json +32 -0
- package/config/claude_desktop_config.json +17 -0
- package/config/cursor_mcp.json +21 -0
- package/config/jetbrains_mcp.json +28 -0
- package/config/kiro_mcp.json +40 -0
- package/config/vscode_mcp.json +24 -0
- package/config/windsurf_mcp.json +18 -0
- package/package.json +51 -0
- package/src/constants.ts +436 -0
- package/src/http-server.ts +186 -0
- package/src/http.ts +190 -0
- package/src/index.ts +702 -0
- package/src/install.ts +441 -0
- package/src/logger.ts +67 -0
- package/src/tools/api-level.ts +198 -0
- package/src/tools/api36-compliance.ts +289 -0
- package/src/tools/architecture.ts +94 -0
- package/src/tools/build-publish.ts +379 -0
- package/src/tools/component.ts +106 -0
- package/src/tools/debugger.ts +111 -0
- package/src/tools/gradle.ts +288 -0
- package/src/tools/kmp.ts +352 -0
- package/src/tools/kotlin-patterns.ts +534 -0
- package/src/tools/large-screen.ts +391 -0
- package/src/tools/m3-expressive.ts +473 -0
- package/src/tools/navigation3.ts +338 -0
- package/src/tools/ondevice-ai.ts +287 -0
- package/src/tools/permissions.ts +445 -0
- package/src/tools/play-policy.ts +229 -0
- package/src/tools/scalability.ts +646 -0
- package/src/tools/search.ts +112 -0
- package/src/tools/testing.ts +460 -0
- package/src/tools/wear.ts +343 -0
- package/src/tools/xr.ts +278 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// Tool 15: Android 16 / API 36 Compliance Checker
|
|
2
|
+
// Google Play mandate: all apps must target API 36 by August 2026.
|
|
3
|
+
// Android 16 removed the ability to lock orientation or restrict resizability on ≥600dp devices.
|
|
4
|
+
export async function androidApi36Compliance(topic) {
|
|
5
|
+
const t = topic.toLowerCase().trim();
|
|
6
|
+
const overview = `
|
|
7
|
+
# Android 16 / API 36 Compliance Reference
|
|
8
|
+
Source: https://developer.android.com/about/versions/16/behavior-changes-all
|
|
9
|
+
|
|
10
|
+
## The Mandate
|
|
11
|
+
|
|
12
|
+
Google Play requires all apps to **target API 36 by August 2026**.
|
|
13
|
+
Android 16 (API 36) introduced breaking changes for large-screen device behavior.
|
|
14
|
+
Apps that fail compliance checks will receive warning badges in Play Store listings
|
|
15
|
+
and may be demoted in search results.
|
|
16
|
+
|
|
17
|
+
## What Changed in Android 16 — The Breaking Rules
|
|
18
|
+
|
|
19
|
+
### 1. Mandatory Resizability on Large Screens (≥600dp)
|
|
20
|
+
On devices with width ≥600dp (tablets, foldables, ChromeOS), apps can NO LONGER:
|
|
21
|
+
- Lock screen orientation
|
|
22
|
+
- Restrict aspect ratio
|
|
23
|
+
- Disable multi-window/resizability
|
|
24
|
+
|
|
25
|
+
The platform **overrides** these flags on ≥600dp devices. Apps must handle any aspect ratio.
|
|
26
|
+
|
|
27
|
+
### 2. Predictive Back — Required for API 36 Targets
|
|
28
|
+
Apps targeting API 36 must implement Predictive Back Gesture.
|
|
29
|
+
The legacy \`OnBackPressedCallback\` alone is insufficient — use \`BackHandler\` in Compose
|
|
30
|
+
or the \`OnBackPressedDispatcher\` with \`createOnBackPressedCallback { }\`.
|
|
31
|
+
|
|
32
|
+
### 3. 16 KB Page Size — Native Code
|
|
33
|
+
Apps with native (NDK/JNI) libraries must be compiled for 16 KB memory page alignment.
|
|
34
|
+
Android Studio shows a lint warning for non-compliant APKs. Failure = crash on affected hardware.
|
|
35
|
+
|
|
36
|
+
## ❌ Manifest Flags That Now FAIL on Large Screens
|
|
37
|
+
|
|
38
|
+
\`\`\`xml
|
|
39
|
+
<!-- ❌ ILLEGAL on ≥600dp devices in Android 16 -->
|
|
40
|
+
<activity
|
|
41
|
+
android:screenOrientation="portrait"
|
|
42
|
+
android:screenOrientation="landscape"
|
|
43
|
+
android:screenOrientation="sensorPortrait"
|
|
44
|
+
android:resizeableActivity="false"
|
|
45
|
+
android:maxAspectRatio="1.86" <!-- Restricts to phone ratio — illegal on large screens -->
|
|
46
|
+
/>
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
## ✅ Correct Manifest for API 36 Compliance
|
|
50
|
+
|
|
51
|
+
\`\`\`xml
|
|
52
|
+
<activity
|
|
53
|
+
android:name=".MainActivity"
|
|
54
|
+
android:exported="true"
|
|
55
|
+
android:windowSoftInputMode="adjustResize"
|
|
56
|
+
<!-- NO screenOrientation — let the system decide -->
|
|
57
|
+
<!-- NO resizeableActivity="false" -->
|
|
58
|
+
<!-- NO maxAspectRatio restriction -->
|
|
59
|
+
>
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
## Games Exception
|
|
63
|
+
|
|
64
|
+
Games are exempt from the mandatory resizability requirement if declared:
|
|
65
|
+
\`\`\`xml
|
|
66
|
+
<application android:appCategory="game">
|
|
67
|
+
\`\`\`
|
|
68
|
+
Users can also opt individual apps into their preferred behavior via device settings.
|
|
69
|
+
|
|
70
|
+
Source: https://developer.android.com/about/versions/16/behavior-changes-all#large-screen
|
|
71
|
+
`;
|
|
72
|
+
const layouts = `
|
|
73
|
+
# Android 16 — Canonical Adaptive Layout Patterns
|
|
74
|
+
Source: https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes
|
|
75
|
+
|
|
76
|
+
## The Four Canonical Layouts (Google's Reference Implementations)
|
|
77
|
+
|
|
78
|
+
### 1. List-Detail (Gmail, Messages)
|
|
79
|
+
\`\`\`kotlin
|
|
80
|
+
// Use Navigation 3 + Scenes API for List-Detail
|
|
81
|
+
// Or use ListDetailPaneScaffold from Material3 Adaptive
|
|
82
|
+
implementation("androidx.compose.material3.adaptive:adaptive:1.1.0")
|
|
83
|
+
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.1.0")
|
|
84
|
+
|
|
85
|
+
@Composable
|
|
86
|
+
fun MailApp() {
|
|
87
|
+
val navigator = rememberListDetailPaneScaffoldNavigator<String>()
|
|
88
|
+
ListDetailPaneScaffold(
|
|
89
|
+
directive = navigator.scaffoldDirective,
|
|
90
|
+
value = navigator.scaffoldValue,
|
|
91
|
+
listPane = {
|
|
92
|
+
InboxPane(onEmailClick = { id ->
|
|
93
|
+
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, id)
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
detailPane = {
|
|
97
|
+
val emailId = navigator.currentDestination?.content
|
|
98
|
+
if (emailId != null) EmailDetailPane(emailId)
|
|
99
|
+
else EmptyDetailPane()
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
\`\`\`
|
|
104
|
+
|
|
105
|
+
### 2. Feed (Photos, Play Store)
|
|
106
|
+
\`\`\`kotlin
|
|
107
|
+
// Adaptive grid — columns based on WindowWidthSizeClass
|
|
108
|
+
@Composable
|
|
109
|
+
fun AdaptiveFeed(items: List<Item>) {
|
|
110
|
+
val windowInfo = currentWindowAdaptiveInfo()
|
|
111
|
+
val columns = when (windowInfo.windowSizeClass.windowWidthSizeClass) {
|
|
112
|
+
WindowWidthSizeClass.COMPACT -> 1
|
|
113
|
+
WindowWidthSizeClass.MEDIUM -> 2
|
|
114
|
+
WindowWidthSizeClass.EXPANDED -> 3
|
|
115
|
+
else -> 1
|
|
116
|
+
}
|
|
117
|
+
LazyVerticalGrid(columns = GridCells.Fixed(columns)) {
|
|
118
|
+
items(items) { item -> FeedCard(item) }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
### 3. Supporting Pane (Docs, Sheets)
|
|
124
|
+
\`\`\`kotlin
|
|
125
|
+
// Primary content + persistent tool panel on large screens
|
|
126
|
+
// SupportingPaneScaffold from Material3 Adaptive
|
|
127
|
+
@Composable
|
|
128
|
+
fun DocumentEditor() {
|
|
129
|
+
val navigator = rememberSupportingPaneScaffoldNavigator()
|
|
130
|
+
SupportingPaneScaffold(
|
|
131
|
+
directive = navigator.scaffoldDirective,
|
|
132
|
+
value = navigator.scaffoldValue,
|
|
133
|
+
mainPane = { DocumentContent() },
|
|
134
|
+
supportingPane = { FormattingPanel() }
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
### 4. Navigation Suite Scaffold (automatic chrome switching)
|
|
140
|
+
\`\`\`kotlin
|
|
141
|
+
// Single API — auto-switches BottomBar → Rail → Drawer based on window size
|
|
142
|
+
@Composable
|
|
143
|
+
fun AppWithAdaptiveNav(content: @Composable () -> Unit) {
|
|
144
|
+
NavigationSuiteScaffold(
|
|
145
|
+
navigationSuiteItems = {
|
|
146
|
+
AppDestination.entries.forEach { destination ->
|
|
147
|
+
item(
|
|
148
|
+
icon = { Icon(destination.icon, contentDescription = destination.label) },
|
|
149
|
+
label = { Text(destination.label) },
|
|
150
|
+
selected = currentDestination == destination,
|
|
151
|
+
onClick = { /* navigate */ }
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
) {
|
|
156
|
+
content()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
Source: https://developer.android.com/guide/topics/large-screens/canonical-app-layouts
|
|
162
|
+
`;
|
|
163
|
+
const checklist = `
|
|
164
|
+
# Android 16 Compliance Checklist — Play Store Large-Screen Quality Tiers
|
|
165
|
+
Source: https://developer.android.com/docs/quality-guidelines/large-screen-app-quality
|
|
166
|
+
|
|
167
|
+
## Tier 3: Large Screen Ready (Minimum — required to avoid warning badge)
|
|
168
|
+
- [ ] No fixed orientation locks (no \`screenOrientation\` in manifest)
|
|
169
|
+
- [ ] No \`resizeableActivity="false"\`
|
|
170
|
+
- [ ] No hardcoded aspect ratio restrictions (\`maxAspectRatio\`)
|
|
171
|
+
- [ ] App does not crash or lose data on configuration change (rotation, resize)
|
|
172
|
+
- [ ] Keyboard/mouse basic support (for ChromeOS)
|
|
173
|
+
- [ ] Multi-window does not break app functionality
|
|
174
|
+
|
|
175
|
+
## Tier 2: Large Screen Optimized
|
|
176
|
+
Everything in Tier 3, plus:
|
|
177
|
+
- [ ] Adaptive layouts using WindowSizeClass breakpoints
|
|
178
|
+
- [ ] NavigationSuiteScaffold (auto-switches nav chrome by window size)
|
|
179
|
+
- [ ] No content clipped or unreachable in landscape/large screen
|
|
180
|
+
- [ ] Proper inset handling (\`WindowInsets\`, \`safeDrawing\`, \`imePadding\`)
|
|
181
|
+
|
|
182
|
+
## Tier 1: Large Screen Differentiated (Editors' Choice eligible)
|
|
183
|
+
Everything in Tier 2, plus:
|
|
184
|
+
- [ ] Multi-pane layout using ListDetailPaneScaffold or Nav3 Scenes
|
|
185
|
+
- [ ] Foldable hinge awareness (FoldingFeature API)
|
|
186
|
+
- [ ] Drag-and-drop support between panes
|
|
187
|
+
- [ ] App-level keyboard shortcuts
|
|
188
|
+
- [ ] Contextual menus on right-click
|
|
189
|
+
|
|
190
|
+
## Critical Code Patterns for Compliance
|
|
191
|
+
|
|
192
|
+
### Configuration Change Survival
|
|
193
|
+
\`\`\`kotlin
|
|
194
|
+
// ViewModel automatically survives rotation — use it
|
|
195
|
+
class MyViewModel : ViewModel() {
|
|
196
|
+
val uiState: StateFlow<MyUiState> = ...
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// For non-ViewModel state, use rememberSaveable
|
|
200
|
+
var selectedId by rememberSaveable { mutableStateOf<String?>(null) }
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
### Inset Handling (Required — no content behind nav bars)
|
|
204
|
+
\`\`\`kotlin
|
|
205
|
+
Scaffold(
|
|
206
|
+
modifier = Modifier.fillMaxSize()
|
|
207
|
+
) { paddingValues ->
|
|
208
|
+
LazyColumn(
|
|
209
|
+
contentPadding = paddingValues, // ALWAYS pass scaffold padding
|
|
210
|
+
modifier = Modifier.imePadding() // keyboard avoidance
|
|
211
|
+
) { ... }
|
|
212
|
+
}
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
215
|
+
### WindowSizeClass — The Correct Import (not the deprecated one)
|
|
216
|
+
\`\`\`kotlin
|
|
217
|
+
// ✅ Correct — currentWindowAdaptiveInfo() from WindowManager
|
|
218
|
+
val adaptiveInfo = currentWindowAdaptiveInfo()
|
|
219
|
+
val widthClass = adaptiveInfo.windowSizeClass.windowWidthSizeClass
|
|
220
|
+
|
|
221
|
+
// ❌ Deprecated — calculateWindowSizeClass(activity) is the old API
|
|
222
|
+
\`\`\`
|
|
223
|
+
|
|
224
|
+
Source: https://developer.android.com/docs/quality-guidelines/large-screen-app-quality
|
|
225
|
+
`;
|
|
226
|
+
const predictiveBack = `
|
|
227
|
+
# Android 16 — Predictive Back Gesture (Required for API 36)
|
|
228
|
+
Source: https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture
|
|
229
|
+
|
|
230
|
+
## What Changed
|
|
231
|
+
|
|
232
|
+
Predictive Back is mandatory for apps targeting API 36. The system now previews
|
|
233
|
+
the "behind" screen during a back swipe before the user completes it.
|
|
234
|
+
|
|
235
|
+
## Implementation in Compose
|
|
236
|
+
|
|
237
|
+
\`\`\`kotlin
|
|
238
|
+
// In AndroidManifest.xml — opt in (required for API 33-35, automatic for API 36)
|
|
239
|
+
<application android:enableOnBackInvokedCallback="true">
|
|
240
|
+
|
|
241
|
+
// In Compose — BackHandler handles predictive back correctly
|
|
242
|
+
@Composable
|
|
243
|
+
fun ScreenWithCustomBack(onBack: () -> Unit) {
|
|
244
|
+
BackHandler(enabled = true) {
|
|
245
|
+
// Custom back logic — e.g., close drawer before popping screen
|
|
246
|
+
onBack()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
\`\`\`
|
|
250
|
+
|
|
251
|
+
## Predictive Back for Multi-Step Flows
|
|
252
|
+
\`\`\`kotlin
|
|
253
|
+
@Composable
|
|
254
|
+
fun MultiStepForm(currentStep: Int, onStepBack: () -> Unit, onExit: () -> Unit) {
|
|
255
|
+
BackHandler(enabled = currentStep > 0) {
|
|
256
|
+
onStepBack() // Go to previous step, not previous screen
|
|
257
|
+
}
|
|
258
|
+
// When currentStep == 0, BackHandler is disabled → system handles back → pops screen
|
|
259
|
+
}
|
|
260
|
+
\`\`\`
|
|
261
|
+
|
|
262
|
+
## Navigation 3 — Predictive Back is Automatic
|
|
263
|
+
When using Navigation 3 (rememberNavBackStack + NavDisplay), predictive back works
|
|
264
|
+
out of the box — NavDisplay registers a BackHandler that removes the last entry.
|
|
265
|
+
No extra setup required.
|
|
266
|
+
|
|
267
|
+
Source: https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture
|
|
268
|
+
`;
|
|
269
|
+
if (t.includes("layout") || t.includes("pane") || t.includes("adaptive") || t.includes("canonical")) {
|
|
270
|
+
return layouts;
|
|
271
|
+
}
|
|
272
|
+
if (t.includes("checklist") || t.includes("quality") || t.includes("tier") || t.includes("play store")) {
|
|
273
|
+
return checklist;
|
|
274
|
+
}
|
|
275
|
+
if (t.includes("back") || t.includes("predictive")) {
|
|
276
|
+
return predictiveBack;
|
|
277
|
+
}
|
|
278
|
+
return overview + "\n\n---\n\n" +
|
|
279
|
+
"**Query topics:** 'layouts' (canonical adaptive patterns), 'checklist' (Play Store quality tiers), " +
|
|
280
|
+
"'predictive back' (API 36 back gesture requirement), 'page size' (16 KB native alignment)\n\n" +
|
|
281
|
+
"Source: https://developer.android.com/about/versions/16";
|
|
282
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool 3 – architecture_reference
|
|
3
|
+
*
|
|
4
|
+
* Retrieves official Android architecture guide content for a given topic.
|
|
5
|
+
* The AI MUST call this before suggesting project structure, patterns,
|
|
6
|
+
* or Jetpack component relationships.
|
|
7
|
+
*/
|
|
8
|
+
import { ARCHITECTURE_GUIDES } from "../constants.js";
|
|
9
|
+
import { secureFetch, extractPageText } from "../http.js";
|
|
10
|
+
function findGuideUrl(topic) {
|
|
11
|
+
const lower = topic.toLowerCase().trim();
|
|
12
|
+
// Exact key match
|
|
13
|
+
if (ARCHITECTURE_GUIDES[lower])
|
|
14
|
+
return ARCHITECTURE_GUIDES[lower];
|
|
15
|
+
// Partial key match
|
|
16
|
+
for (const [key, url] of Object.entries(ARCHITECTURE_GUIDES)) {
|
|
17
|
+
if (lower.includes(key) || key.includes(lower))
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Core handler for architecture_reference tool.
|
|
24
|
+
*/
|
|
25
|
+
export async function architectureReference(topic) {
|
|
26
|
+
if (!topic || topic.trim().length < 2) {
|
|
27
|
+
return "ERROR: Topic must be at least 2 characters.";
|
|
28
|
+
}
|
|
29
|
+
const sanitized = topic.trim().slice(0, 200);
|
|
30
|
+
const guideUrl = findGuideUrl(sanitized);
|
|
31
|
+
const header = `## AndroJack Architecture Reference\n` +
|
|
32
|
+
`**Topic:** "${sanitized}"\n\n`;
|
|
33
|
+
if (!guideUrl) {
|
|
34
|
+
// Fallback: search developer.android.com
|
|
35
|
+
const searchUrl = `https://developer.android.com/s/results?q=${encodeURIComponent(sanitized)}`;
|
|
36
|
+
try {
|
|
37
|
+
const html = await secureFetch(searchUrl);
|
|
38
|
+
const text = extractPageText(html, 2500);
|
|
39
|
+
return (header +
|
|
40
|
+
`**No direct guide mapped. Showing search results from developer.android.com:**\n` +
|
|
41
|
+
`**URL:** ${searchUrl}\n\n` +
|
|
42
|
+
text +
|
|
43
|
+
`\n\n> 📐 GROUNDING GATE: Review the architecture guide above before proposing any structure.`);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return (header +
|
|
47
|
+
`Could not fetch guide. Search manually: ${searchUrl}\n` +
|
|
48
|
+
`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const html = await secureFetch(guideUrl);
|
|
53
|
+
const text = extractPageText(html, 3000);
|
|
54
|
+
return (header +
|
|
55
|
+
`**Official Guide:** ${guideUrl}\n\n` +
|
|
56
|
+
text +
|
|
57
|
+
`\n\n---\n` +
|
|
58
|
+
`> 📐 GROUNDING GATE: Architecture code must align with the official guide above.`);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
return (header +
|
|
62
|
+
`**Official Guide:** ${guideUrl}\n` +
|
|
63
|
+
`**Fetch failed:** ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
64
|
+
`Please open the guide manually: ${guideUrl}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Returns the full list of supported architecture topics for discovery.
|
|
69
|
+
*/
|
|
70
|
+
export function listArchitectureTopics() {
|
|
71
|
+
const topics = Object.keys(ARCHITECTURE_GUIDES).sort();
|
|
72
|
+
return (`## Supported Architecture Topics\n\n` +
|
|
73
|
+
topics.map((t) => `- \`${t}\``).join("\n") +
|
|
74
|
+
`\n\nPass any of these to \`architecture_reference\` for official guide content.`);
|
|
75
|
+
}
|