diff --git a/package.json b/package.json
index a5e96ed46..2082900bc 100644
--- a/package.json
+++ b/package.json
@@ -848,6 +848,12 @@
             "type": "string",
             "default": "http://localhost:8080/api",
             "description": "The base URL of the AI service."
+          },
+          "runme.app.faqUrl": {
+            "type": "string",
+            "scope": "window",
+            "default": "https://docs.runme.dev/faq",
+            "markdownDescription": "Frequently Asked Questions page"
           }
         }
       }
diff --git a/src/extension/executors/aws.ts b/src/extension/executors/aws.ts
index 6727c9f2a..141b153fc 100644
--- a/src/extension/executors/aws.ts
+++ b/src/extension/executors/aws.ts
@@ -14,7 +14,7 @@ import { resolveProgramOptionsScript } from './runner'
 import { IKernelExecutor } from '.'
 
 export const aws: IKernelExecutor = async (executor) => {
-  const { cellText, exec, runner, runnerEnv, doc, outputs, context } = executor
+  const { cellText, exec, runner, runnerEnv, doc, outputs, kernel } = executor
 
   const annotations = getAnnotations(exec.cell)
 
@@ -41,67 +41,14 @@ export const aws: IKernelExecutor = async (executor) => {
       cellId,
     })
 
-    // todo(sebastian): move down into kernel?
     switch (programOptions.exec?.type) {
       case 'script':
-        {
-          programOptions.exec.script = 'echo $AWS_PROFILE'
-        }
+        programOptions.exec.script = 'echo $AWS_PROFILE'
         break
     }
 
-    const program = await runner.createProgramSession(programOptions)
-    context.subscriptions.push(program)
-
-    let execRes: string | undefined
-    const onData = (data: string | Uint8Array) => {
-      if (execRes === undefined) {
-        execRes = ''
-      }
-      execRes += data.toString()
-    }
-
-    program.onDidWrite(onData)
-    program.onDidErr(onData)
-    program.run()
-
-    const success = await new Promise<boolean>((resolve, reject) => {
-      program.onDidClose(async (code) => {
-        if (code !== 0) {
-          return resolve(false)
-        }
-        return resolve(true)
-      })
-
-      program.onInternalErr((e) => {
-        reject(e)
-      })
-
-      const exitReason = program.hasExited()
-
-      // unexpected early return, likely an error
-      if (exitReason) {
-        switch (exitReason.type) {
-          case 'error':
-            {
-              reject(exitReason.error)
-            }
-            break
-
-          case 'exit':
-            {
-              resolve(exitReason.code === 0)
-            }
-            break
-
-          default: {
-            resolve(false)
-          }
-        }
-      }
-    })
-
-    const profile = success ? execRes?.trim() : 'default'
+    const result = await kernel.runProgram(programOptions)
+    const profile = result.code === 0 ? result.output : 'default'
     credentials = fromIni({ profile })
 
     switch (awsResolver.view) {
diff --git a/src/extension/extension.ts b/src/extension/extension.ts
index 5cb8c14b7..50478738a 100644
--- a/src/extension/extension.ts
+++ b/src/extension/extension.ts
@@ -15,6 +15,7 @@ import Channel from 'tangle/webviews'
 
 import { NotebookUiEvent, Serializer, SyncSchema } from '../types'
 import {
+  getFaqUrl,
   getForceNewWindowConfig,
   getRunmeAppUrl,
   getSessionOutputs,
@@ -80,6 +81,7 @@ import { NotebookCellStatusBarProvider } from './provider/cellStatusBar/notebook
 import { SessionOutputCellStatusBarProvider } from './provider/cellStatusBar/sessionOutput'
 import * as generate from './ai/generate'
 import * as manager from './ai/manager'
+
 export class RunmeExtension {
   protected serializer?: SerializerBase
 
@@ -129,6 +131,23 @@ export class RunmeExtension {
      */
     try {
       await server.launch()
+      kernel.runProgram('echo $0').then(async ({ output }) => {
+        const supportedShells = ['zsh', 'bash']
+        const isSupported = supportedShells.some((sh) => output?.includes(sh))
+
+        if (isSupported) {
+          return
+        }
+
+        const showMore = 'Show more'
+        const answer = await window.showWarningMessage('Unsupported shell', showMore)
+
+        if (answer === showMore) {
+          const dashboardUri = getFaqUrl('supported-shells')
+          const uri = Uri.parse(dashboardUri)
+          env.openExternal(uri)
+        }
+      })
     } catch (e) {
       // Unrecoverable error happened
       if (e instanceof KernelServerError) {
diff --git a/src/extension/kernel.ts b/src/extension/kernel.ts
index 3146330c2..1a42fa8d6 100644
--- a/src/extension/kernel.ts
+++ b/src/extension/kernel.ts
@@ -72,7 +72,7 @@ import {
 import { getSystemShellPath, isShellLanguage } from './executors/utils'
 import './wasm/wasm_exec.js'
 import { RpcError } from './grpc/client'
-import { IRunner, IRunnerReady } from './runner'
+import { IRunner, IRunnerReady, RunnerExitReason, RunProgramOptions } from './runner'
 import { IRunnerEnvironment } from './runner/environment'
 import { IKernelRunnerOptions, executeRunner } from './executors/runner'
 import { ITerminalState, NotebookTerminalType } from './terminal/terminalState'
@@ -1119,4 +1119,75 @@ export class Kernel implements Disposable {
   public getPlainCache(cacheId: string): Promise<Uint8Array> | undefined {
     return this.serializer?.getPlainCache(cacheId)
   }
+
+  async runProgram(program?: RunProgramOptions | string) {
+    let programOptions: RunProgramOptions
+
+    if (typeof program === 'object') {
+      programOptions = program
+    } else if (typeof program === 'string') {
+      programOptions = {
+        programName: '',
+        background: false,
+        exec: {
+          type: 'script',
+          script: program,
+        },
+        languageId: 'sh',
+        storeLastOutput: false,
+        tty: true,
+      }
+    } else {
+      return Promise.reject(new Error('Invalid runProgram arguments'))
+    }
+    const runner = this.runner!
+    const programSession = await runner.createProgramSession(programOptions)
+    this.context.subscriptions.push(programSession)
+
+    let execRes: string | undefined
+    const onData = (data: string | Uint8Array) => {
+      if (execRes === undefined) {
+        execRes = ''
+      }
+      execRes += data.toString()
+    }
+
+    programSession.onDidWrite(onData)
+    programSession.onDidErr(onData)
+    programSession.run()
+
+    return new Promise<{ exitReason?: RunnerExitReason; code?: number | void; output?: string }>(
+      (resolve, reject) => {
+        programSession.onDidClose(async (code) => {
+          return resolve({ code, output: execRes?.trim() })
+        })
+
+        programSession.onInternalErr((e) => {
+          reject(e)
+        })
+
+        const exitReason = programSession.hasExited()
+
+        if (exitReason) {
+          switch (exitReason.type) {
+            case 'error':
+              {
+                reject({ exitReason, output: execRes?.trim() })
+              }
+              break
+
+            case 'exit':
+              {
+                resolve({ exitReason, code: exitReason.code, output: execRes?.trim() })
+              }
+              break
+
+            default: {
+              resolve({ exitReason, output: execRes?.trim() })
+            }
+          }
+        }
+      },
+    )
+  }
 }
diff --git a/src/utils/configuration.ts b/src/utils/configuration.ts
index 238f5f546..a5f5f970e 100644
--- a/src/utils/configuration.ts
+++ b/src/utils/configuration.ts
@@ -23,6 +23,7 @@ const DEFAULT_WORKSPACE_FILE_ORDER = ['.env.local', '.env']
 const DEFAULT_RUNME_APP_API_URL = 'https://platform.stateful.com'
 const DEFAULT_RUNME_BASE_DOMAIN = 'platform.stateful.com'
 const DEFAULT_RUNME_REMOTE_DEV = 'staging.platform.stateful.com'
+const DEFAULT_FAQ_URL = 'https://docs.runme.dev/faq'
 const APP_LOOPBACKS = ['127.0.0.1', 'localhost']
 const APP_LOOPBACK_MAPPING = new Map<string, string>([
   ['api.', ':4000'],
@@ -90,6 +91,7 @@ const configurationSchema = {
     maskOutputs: z.boolean().default(true),
     loginPrompt: z.boolean().default(true),
     platformAuth: z.boolean().default(false),
+    faqUrl: z.string().default(DEFAULT_FAQ_URL),
   },
 }
 
@@ -432,6 +434,11 @@ const isPlatformAuthEnabled = (): boolean => {
   return getCloudConfigurationValue('platformAuth', false)
 }
 
+const getFaqUrl = (hash: string): string => {
+  const baseUrl = getCloudConfigurationValue('faqUrl', DEFAULT_FAQ_URL)
+  return `${baseUrl}#${hash}`
+}
+
 export {
   enableServerLogs,
   getActionsOpenViewInEditor,
@@ -462,4 +469,5 @@ export {
   getSessionOutputs,
   getMaskOutputs,
   getLoginPrompt,
+  getFaqUrl,
 }