NousResearch/hermes-agent

Clone profile from any source via dropdown

Cluster 23746 · hermes cli. Data as of

Nothing open on GitHub. Fix merged in #45630 on 13 Jun 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #45630. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #45630. That is separate from the fix event.
Still open
Nothing open on GitHub.

These issues and PRs relate to adding or requesting a feature that allows cloning a new profile from any existing profile, replacing a checkbox with a dropdown selector. The theme is consistent across a dashboard UI change and a corresponding profile backend feature.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #45630 feat(profile): clone profiles from any source

    merged to main

    files: apps/desktop/src/app/chat/sidebar/profile-switcher.tsx, apps/desktop/src/app/profiles/create-profile-dialog.tsx, apps/desktop/src/app/profiles/index.tsx, apps/desktop/src/i18n/en.ts, apps/desktop/src/i18n/ja.ts, apps/desktop/src/i18n/types.ts, apps/desktop/src/i18n/zh-hant.ts, apps/desktop/src/i18n/zh.ts, apps/desktop/src/types/hermes.ts, docs/design/profile-builder.md, hermes_cli/main.py, hermes_cli/profiles.py, hermes_cli/subcommands/profile.py, hermes_cli/web_server.py, scripts/release.py, tests/hermes_cli/test_web_server.py, web/src/i18n/af.ts, web/src/i18n/de.ts, web/src/i18n/en.ts, web/src/i18n/es.ts (+24 more)

    Show the diff
    • apps/desktop/src/app/chat/sidebar/profile-switcher.tsx
      @@ -284,6 +284,7 @@ export function ProfileRail() {
                 selectProfile(name)
               }}
               open={createOpen}
      +        profiles={profiles}
             />
       
             <RenameProfileDialog
    • apps/desktop/src/app/profiles/create-profile-dialog.tsx
      @@ -2,14 +2,15 @@ import { useEffect, useState } from 'react'
       
       import { ActionStatus } from '@/components/ui/action-status'
       import { Button } from '@/components/ui/button'
      -import { Checkbox } from '@/components/ui/checkbox'
       import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
       import { Input } from '@/components/ui/input'
      +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
       import { Textarea } from '@/components/ui/textarea'
       import { createProfile, updateProfileSoul } from '@/hermes'
       import { useI18n } from '@/i18n'
       import { AlertTriangle } from '@/lib/icons'
       import { cn } from '@/lib/utils'
      +import type { ProfileInfo } from '@/types/hermes'
       
       const PROFILE_NAME_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/
       
      @@ -23,16 +24,18 @@ export function isValidProfileName(name: string): boolean {
       export function CreateProfileDialog({
         onClose,
         onCreated,
      -  open
      +  open,
      +  profiles = []
       }: {
         onClose: () => void
         onCreated?: (name: string) => Promise<void> | void
         open: boolean
      +  profiles?: ProfileInfo[]
       }) {
         const { t } = useI18n()
         const p = t.profiles
         const [name, setName] = useState('')
      -  const [cloneFromDefault, setCloneFromDefault] = useState(true)
      +  const [cloneFrom, setCloneFrom] = useState<null | string>('default')
         const [soul, setSoul] = useState('')
         const [status, setStatus] = useState<'done' | 'idle' | 'saving'>('idle')
         const [error, setError] = useState<null | string>(null)
      @@ -43,7 +46,7 @@ export function CreateProfileDialog({
           }
       
           setName('')
      -    setCloneFromDefault(true)
      +    setCloneFrom('default')
           setSoul('')
           setError(null)
           setStatus('idle')
      @@ -66,7 +69,7 @@ export function CreateProfileDialog({
           setError(null)
       
           try {
      -      await createProfile({ name: trimmed, clone_from_default: cloneFromDefault })
      +      await createProfile({ name: trimmed, clone_from: cloneFrom })
       
             if (soul.trim()) {
               await updateProfileSoul(trimmed, soul)
      @@ -107,17 +110,25 @@ export function CreateProfileDialog({
                   </p>
                 </div>
       
      -          <label className="flex cursor-pointer select-none items-start gap-2.5 px-0.5 py-1">
      -            <Checkbox
      -              checked={cloneFromDefault}
      -              className="mt-0.5 shrink-0"
      -              onCheckedChange={checked => setCloneFromDefault(checked === true)}
      -            />
      -            <span className="grid gap-0.5 leading-snug">
      -              <span className="text-sm font-medium">{p.cloneFromDefault}</span>
      -              <span className="text-xs text-muted-foreground">{p.cloneFromDefaultDesc}</span>
      -            </span>
      -          </label>
      +          <div className="grid gap-1.5">
      +            <label className="text-xs font-medium" htmlFor="new-profile-clone-from">
      +              {p.cloneFrom}
      +            </label>
      +            <Select onValueChange={value => setCloneFrom(value === '__none__' ? null : value)} value={cloneFrom ?? '__none__'}>
      +              <SelectTrigger className="h-9 rounded-md" id="new-profile-clone-from">
      +                <SelectValue />
      +              </SelectTrigger>
      +              <SelectContent>
      +                <SelectItem value="__none__">{p.cloneFromNone}</SelectItem>
      +                {profiles.map(profile => (
      +                  <SelectItem key={profile.name} value={profile.name}>
      +                    {profile.name}
      +                  </SelectItem>
      +                ))}
      +              </SelectContent>
      +            </Select>
      +            <p className="text-xs text-muted-foreground">{p.cloneFromDesc}</p>
      +          </div>
       
                 <div className="grid gap-1.5">
                   <label className="text-xs font-medium" htmlFor="new-profile-soul">
      @@ -127,7 +138,7 @@ export function CreateProfileDialog({
                     className="min-h-28 font-mono text-xs leading-5"
                     id="new-profile-soul"
                     onChange={event => setSoul(event.target.value)}
      -              placeholder={p.soulPlaceholder(cloneFromDefault ? p.soulPlaceholderCloned : p.soulPlaceholderEmpty)}
      +              placeholder={p.soulPlaceholder(cloneFrom ? p.soulPlaceholderCloned : p.soulPlaceholderEmpty)}
                     value={soul}
                   />
                 </div>
    • apps/desktop/src/app/profiles/index.tsx
      @@ -12,6 +12,7 @@ import {
         DialogTitle
       } from '@/components/ui/dialog'
       import { Input } from '@/components/ui/input'
      +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
       import { Textarea } from '@/components/ui/textarea'
       import {
         createProfile,
      @@ -82,14 +83,14 @@ export function ProfilesView({ onClose }: ProfilesViewProps) {
         }, [profiles, selectedName])
       
         const handleCreate = useCallback(
      -    async (name: string, cloneFromDefault: boolean) => {
      +    async (name: string, cloneFrom: null | string) => {
             const trimmed = name.trim()
       
             if (!isValidProfileName(trimmed)) {
               throw new Error(p.nameHint)
             }
       
      -      await createProfile({ name: trimmed, clone_from_default: cloneFromDefault })
      +      await createProfile({ name: trimmed, clone_from: cloneFrom })
             notify({ kind: 'success', title: p.created, message: trimmed })
             setSelectedName(trimmed)
             await refresh()
      @@ -180,8 +181,9 @@ export function ProfilesView({ onClose }: ProfilesViewProps) {
       
             <CreateProfileDialog
                 onClose={() => setCreateOpen(false)}
      -          onCreate={async (name, cloneFromDefault) => handleCreate(name, cloneFromDefault)}
      +          onCreate={async (name, cloneFrom) => handleCreate(name, cloneFrom)}
                 open={createOpen}
      +          profiles={profiles ?? []}
               />
       
               <Dialog onOpenChange={open => !open && !deleting && setPendingDelete(null)} open={pendingDelete !== null}>
      @@ -453,16 +455,18 @@ function SoulEditor({ profileName }: { profileName: string }) {
       function CreateProfileDialog({
         onClose,
         onCreate,
      -  open
      +  open,
      +  profiles
       }: {
         onClose: () => void
      -  onCreate: (name: string, cloneFromDefault: boolean) => Promise<void>
      +  onCreate: (name: string, cloneFrom: null | string) => Promise<void>
         open: boolean
      +  profiles: ProfileInfo[]
       }) {
         const { t } = useI18n()
         const p = t.profiles
         const [name, setName] = useState('')
      -  const [cloneFromDefault, setCloneFromDefault] = useState(true)
      +  const [cloneFrom, setCloneFrom] = useState<null | string>('default')
         const [saving, setSaving] = useState(false)
         const [error, setError] = useState<null | string>(null)
       
      @@ -472,7 +476,7 @@ function CreateProfileDialog({
           }
       
           setName('')
      -    setCloneFromDefault(true)
      +    setCloneFrom('default')
           setError(null)
           setSaving(false)
         }, [open])
      @@ -493,7 +497,7 @@ function CreateProfileDialog({
           setError(null)
       
           try {
      -      await onCreate(trimmed, cloneFromDefault)
      +      await onCreate(trimmed, cloneFrom)
             onClose()
           } catch (err) {
             setError(err instanceof Error ? err.message : p.failedCreate)
      @@ -528,18 +532,25 @@ function CreateProfileDialog({
                   </p>
                 </div>
       
      -          <label className="flex cursor-pointer items-center gap-2 rounded-md border border-border/40 bg-background/50 px-3 py-2 text-sm">
      -            <input
      -              checked={cloneFromDefault}
      -              className="size-4 accent-primary"
      -              onChange={event => setCloneFromDefault(event.target.checked)}
      -              type="checkbox"
      -            />
      -            <span>
      -              <span className="font-medium">{p.cloneFromDefault}</span>
      -              <span className="ml-2 text-xs text-muted-foreground">{p.cloneFromDefaultDesc}</span>
      -            </span>
      -          </label>
      +          <div className="grid gap-1.5">
      +            <label className="text-xs font-medium" htmlFor="new-profile-clone-from">
      +              {p.cloneFrom}
      +            </label>
      +            <Select onValueChange={value => setCloneFrom(value === '__none__' ? null : value)} value={cloneFrom ?? '__none__'}>
      +              <SelectTrigger className="h-9 rounded-md" id="new-profile-clone-from">
      +                <SelectValue />
      +              </SelectTrigger>
      +              <SelectContent>
      +                <SelectItem value="__none__">{p.cloneFromNone}</SelectItem>
      +                {profiles.map(profile => (
      +                  <SelectItem key={profile.name} value={profile.name}>
      +                    {profile.name}
      +                  </SelectItem>
      +                ))}
      +              </SelectContent>
      +            </Select>
      +            <p className="text-xs text-muted-foreground">{p.cloneFromDesc}</p>
      +          </div>
       
                 {error && (
                   <div className="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
    • apps/desktop/src/i18n/en.ts
      @@ -903,6 +903,9 @@ export const en: Translations = {
           deleting: 'Deleting...',
           createDesc: 'Profiles are independent Hermes environments: separate config, skills, and SOUL.md.',
           nameLabel: 'Name',
      +    cloneFrom: 'Clone from',
      +    cloneFromNone: 'None (blank)',
      +    cloneFromDesc: 'Copies config, skills, and SOUL.md from the selected source profile.',
           cloneFromDefault: 'Clone from default',
           cloneFromDefaultDesc: 'Copy config, skills, and SOUL.md from your default profile.',
           invalidName: hint => `Invalid name. ${hint}`,
    • apps/desktop/src/i18n/ja.ts
      @@ -1041,6 +1041,9 @@ export const ja = defineLocale({
           deleting: '削除中...',
           createDesc: 'プロファイルは独立した Hermes 環境です:設定、スキル、SOUL.md が別々になります。',
           nameLabel: '名前',
      +    cloneFrom: '複製元',
      +    cloneFromNone: 'なし(空)',
      +    cloneFromDesc: '選択したプロファイルから設定、スキル、SOUL.md をコピーします。',
           cloneFromDefault: 'デフォルトプロファイルから設定を複製',
           cloneFromDefaultDesc: 'デフォルトプロファイルから設定、スキル、SOUL.md をコピーします。',
           invalidName: hint => `無効なプロファイル名。${hint}`,
    • apps/desktop/src/i18n/types.ts
      @@ -695,6 +695,9 @@ export interface Translations {
           deleting: string
           createDesc: string
           nameLabel: string
      +    cloneFrom: string
      +    cloneFromNone: string
      +    cloneFromDesc: string
           cloneFromDefault: string
           cloneFromDefaultDesc: string
           invalidName: (hint: string) => string
    • apps/desktop/src/i18n/zh-hant.ts
      @@ -999,6 +999,9 @@ export const zhHant = defineLocale({
           deleting: '刪除中…',
           createDesc: '設定檔是獨立的 Hermes 環境:各自擁有獨立的設定、技能和 SOUL.md。',
           nameLabel: '名稱',
      +    cloneFrom: '複製來源',
      +    cloneFromNone: '無(空白)',
      +    cloneFromDesc: '從選取的來源設定檔複製設定、技能和 SOUL.md。',
           cloneFromDefault: '從預設設定檔複製設定',
           cloneFromDefaultDesc: '從您的預設設定檔複製設定、技能和 SOUL.md。',
           invalidName: hint => `設定檔名稱無效。${hint}`,
    • apps/desktop/src/i18n/zh.ts
      @@ -1092,6 +1092,9 @@ export const zh: Translations = {
           deleting: '删除中…',
           createDesc: '配置档案是相互独立的 Hermes 环境:各自拥有独立的配置、技能和 SOUL.md。',
           nameLabel: '名称',
      +    cloneFrom: '克隆来源',
      +    cloneFromNone: '无(空白)',
      +    cloneFromDesc: '从选中的来源配置档案复制配置、技能和 SOUL.md。',
           cloneFromDefault: '从默认档案克隆',
           cloneFromDefaultDesc: '从你的默认配置档案复制配置、技能和 SOUL.md。',
           invalidName: hint => `名称无效。${hint}`,
    • apps/desktop/src/types/hermes.ts
      @@ -470,7 +470,7 @@ export interface CronJobUpdates {
       
       export interface ProfileCreatePayload {
         clone_all?: boolean
      -  clone_from?: string
      +  clone_from?: null | string
         clone_from_default?: boolean
         name: string
         no_skills?: boolean
    • docs/design/profile-builder.md
      @@ -85,6 +85,8 @@ Extend `ProfileCreate` and the create endpoint — no new endpoints, no rewrite:
       ```python
       class ProfileCreate(BaseModel):
           name: str
      +    clone_from: Optional[str] = None
      +    # Backward compatibility for older dashboard/desktop clients.
           clone_from_default: bool = False
           clone_all: bool = False
           no_skills: bool = False
    • hermes_cli/main.py
      @@ -9860,19 +9860,20 @@ def cmd_profile(args):
       
               try:
                   clone_from = getattr(args, "clone_from", None)
      +            clone_config = clone or clone_from is not None
       
                   profile_dir = create_profile(
                       name=name,
                       clone_from=clone_from,
                       clone_all=clone_all,
      -                clone_config=clone,
      +                clone_config=clone_config,
                       no_alias=no_alias,
                       no_skills=no_skills,
                       description=getattr(args, "description", None),
                   )
                   print(f"\nProfile '{name}' created at {profile_dir}")
       
      -            if clone or clone_all:
      +            if clone_config or clone_all:
                       source_label = (
                           getattr(args, "clone_from", None) or get_active_profile_name()
                       )
      @@ -9886,8 +9887,8 @@ def cmd_profile(args):
                               f"Cloned config, .env, SOUL.md, and skills from {source_label}."
                           )
       
      -            # Auto-clone Honcho config for the new profile (only with --clone/--clone-all)
      -            if clone or clone_all:
      +            # Auto-clone Honcho config for the new profile (only with clone operations)
      +            if clone_config or clone_all:
                       try:
                           from plugins.memory.honcho.cli import clone_honcho_for_profile
       
      @@ -9896,10 +9897,10 @@ def cmd_profile(args):
                       except Exception:
                           pass  # Honcho plugin not installed or not configured
       
      -            # Seed bundled skills (skip if --clone-all already copied them, or
      -            # if --no-skills was passed — in which case seed_profile_skills()
      -            # honors the marker file and returns skipped_opt_out=True).
      -            if not clone_all:
      +            # Seed bundled skills for fresh profiles only. Clone operations
      +            # already copied the source profile's skills, including any
      +            # user-installed or intentionally removed skills.
      +            if not (clone_config or clone_all):
                       result = seed_profile_skills(profile_dir)
                       if result and result.get("skipped_opt_out"):
                           print(
    • hermes_cli/profiles.py
      @@ -784,9 +784,9 @@ def create_profile(
           Path
               The newly created profile directory.
           """
      -    if no_skills and (clone_config or clone_all):
      +    if no_skills and (clone_from is not None or clone_config or clone_all):
               raise ValueError(
      -            "--no-skills is mutually exclusive with --clone / --clone-all "
      +            "--no-skills is mutually exclusive with --clone / --clone-from / --clone-all "
                   "(cloning explicitly copies skills from the source profile)."
               )
           canon = normalize_profile_name(name)
    • hermes_cli/subcommands/profile.py
      @@ -35,17 +35,17 @@ def build_profile_parser(subparsers, *, cmd_profile: Callable) -> None:
           profile_create.add_argument(
               "--clone",
               action="store_true",
      -        help="Copy config.yaml, .env, SOUL.md from active profile",
      +        help="Copy config.yaml, .env, SOUL.md, and skills from active profile",
           )
           profile_create.add_argument(
               "--clone-all",
               action="store_true",
      -        help="Full copy of active profile (all state)",
      +        help="Full copy of active profile (all state, excluding per-profile history)",
           )
           profile_create.add_argument(
               "--clone-from",
               metavar="SOURCE",
      -        help="Source profile to clone from (default: active)",
      +        help="Source profile to clone from; implies --clone unless --clone-all is set",
           )
           profile_create.add_argument(
               "--no-alias", action="store_true", help="Skip wrapper script creation"
    • hermes_cli/web_server.py
      @@ -8493,15 +8493,13 @@ def _run():
       
       class ProfileCreate(BaseModel):
           name: str
      +    clone_from: Optional[str] = None
      +    # Backward compatibility for older dashboard/desktop clients. New clients
      +    # send clone_from="default" (or another profile name) explicitly.
           clone_from_default: bool = False
           clone_all: bool = False
           no_skills: bool = False
           description: Optional[str] = None
      -    # Explicit source profile to clone from (e.g. duplicating an existing
      -    # profile). When set, it takes precedence over ``clone_from_default``,
      -    # which always sources from "default". ``clone_all`` still selects a full
      -    # state copytree vs. a config/skills/SOUL copy.
      -    clone_from: Optional[str] = None
           provider: Optional[str] = None
           model: Optional[str] = None
           # Profile-builder additions — all optional, all applied best-effort AFTER
      @@ -8773,10 +8771,16 @@ async def create_profile_endpoint(body: ProfileCreate):
               clone = True
               clone_from = explicit_source
               clone_config = not body.clone_all
      +    elif body.clone_all:
      +        # Preserve the dashboard's historical clone-all behavior: a full-copy
      +        # request with no explicit dropdown source copies from default.
      +        clone = True
      +        clone_from = "default"
      +        clone_config = False
           else:
      -        clone = body.clone_from_default or body.clone_all
      +        clone = body.clone_from_default
               clone_from = "default" if clone else None
      -        clone_config = body.clone_from_default and not body.clone_all
      +        clone_config = clone
           try:
               path = profiles_mod.create_profile(
                   name=body.name,
    • scripts/release.py
      @@ -494,6 +494,7 @@
           "[redacted:email]": "nik1t7n",
           "[email protected]": "nik1t7n",
           "[redacted:email]": "suncokret12",
      +    "[redacted:email]": "WompaJango",
           "[redacted:email]": "mioimotoai-lgtm",
           "[redacted:email]": "heyitsaamir",
           "[redacted:email]": "johnncenae",
    • tests/hermes_cli/test_web_server.py
      @@ -2633,7 +2633,7 @@ def test_profiles_create_creates_wrapper_alias_when_safe(self, monkeypatch, tmp_
       
               resp = self.client.post(
                   "/api/profiles",
      -            json={"name": "writer", "clone_from_default": False},
      +            json={"name": "writer", "clone_from": None},
               )
       
               assert resp.status_code == 200
      @@ -2641,7 +2641,7 @@ def test_profiles_create_creates_wrapper_alias_when_safe(self, monkeypatch, tmp_
               assert wrapper_path.exists()
               assert wrapper_path.read_text() == '#!/bin/sh\nexec hermes -p writer "$@"\n'
       
      -    def test_profiles_create_with_clone_from_default_copies_default_skills(self, monkeypatch):
      +    def test_profiles_create_with_clone_from_copies_source_skills(self, monkeypatch):
               from hermes_constants import get_hermes_home
               import hermes_cli.profiles as profiles_mod
       
      @@ -2652,7 +2652,7 @@ def test_profiles_create_with_clone_from_default_copies_default_skills(self, mon
       
               resp = self.client.post(
                   "/api/profiles",
      -            json={"name": "cloned", "clone_from_default": True},
      +            json={"name": "cloned", "clone_from": "default"},
               )
       
               assert resp.status_code == 200
      @@ -2685,6 +2685,28 @@ def test_profiles_create_with_clone_from_duplicates_source(self, monkeypatch):
               )
               assert cloned_skill.exists()
       
      +    def test_profiles_create_clone_all_from_named_source(self, monkeypatch):
      +        from hermes_constants import get_hermes_home
      +        import hermes_cli.profiles as profiles_mod
      +
      +        monkeypatch.setattr(profiles_mod, "create_wrapper_script", lambda name: None)
      +
      +        assert self.client.post("/api/profiles", json={"name": "full-src"}).status_code == 200
      +        source_dir = get_hermes_home() / "profiles" / "full-src"
      +        (source_dir / "config.yaml").write_text("model:\n  provider: source-only\n", encoding="utf-8")
      +        (source_dir / "workspace" / "artifact.txt").parent.mkdir(parents=True, exist_ok=True)
      +        (source_dir / "workspace" / "artifact.txt").write_text("copied", encoding="utf-8")
      +
      +        resp = self.client.post(
      +            "/api/profiles",
      +            json={"name": "full-copy", "clone_from": "full-src", "clone_all": True},
      +        )
      +
      +        assert resp.status_code == 200
      +        target_dir = get_hermes_home() / "profiles" / "full-copy"
      +        assert (target_dir / "config.yaml").read_text(encoding="utf-8") == "model:\n  provider: source-only\n"
      +        assert (target_dir / "workspace" / "artifact.txt").read_text(encoding="utf-8") == "copied"
      +
           def test_profiles_create_without_clone_seeds_bundled_skills(self, monkeypatch):
               from hermes_constants import get_hermes_home
               import hermes_cli.profiles as profiles_mod
      @@ -2701,7 +2723,7 @@ def fake_seed(profile_dir, quiet=False):
       
               resp = self.client.post(
                   "/api/profiles",
      -            json={"name": "fresh", "clone_from_default": False},
      +            json={"name": "fresh", "clone_from": None},
               )
       
               assert resp.status_code == 200
    • web/src/i18n/af.ts
      @@ -286,8 +286,8 @@ export const af: Translations = {
           nameRequired: "Naam word vereis",
           nameRule:
             "Slegs kleinletters, syfers, _ en -; moet met 'n letter of syfer begin; tot 64 karakters.",
      -    invalidName: "Ongeldige profielnaam",
      -    cloneFromDefault: "Kloon konfigurasie vanaf verstekprofiel",
      +    invalidName: "Ongeldige profielnaam",    cloneFrom: "Kloon konfigurasie vanaf profiel",
      +    cloneFromNone: "Geen (leeg)",
           allProfiles: "Profiele",
           noProfiles: "Geen profiele gevind nie.",
           defaultBadge: "verstek",
    • web/src/i18n/de.ts
      @@ -286,8 +286,8 @@ export const de: Translations = {
           nameRequired: "Name ist erforderlich",
           nameRule:
             "Nur Kleinbuchstaben, Ziffern, _ und -; muss mit einem Buchstaben oder einer Ziffer beginnen; maximal 64 Zeichen.",
      -    invalidName: "Ungültiger Profilname",
      -    cloneFromDefault: "Konfiguration vom Standardprofil klonen",
      +    invalidName: "Ungültiger Profilname",    cloneFrom: "Konfiguration klonen von",
      +    cloneFromNone: "Keine (leer)",
           allProfiles: "Profile",
           noProfiles: "Keine Profile gefunden.",
           defaultBadge: "Standard",
    • web/src/i18n/en.ts
      @@ -297,7 +297,8 @@ export const en: Translations = {
           nameRule:
             "Lowercase letters, digits, _ and - only; must start with a letter or digit; up to 64 characters.",
           invalidName: "Invalid profile name",
      -    cloneFromDefault: "Clone config from default profile",
      +    cloneFrom: "Clone config from",
      +    cloneFromNone: "None (blank)",
           allProfiles: "Profiles",
           noProfiles: "No profiles found.",
           defaultBadge: "default",
    • web/src/i18n/es.ts
      @@ -287,7 +287,8 @@ export const es: Translations = {
           nameRule:
             "Solo letras minúsculas, dígitos, _ y -; debe comenzar con una letra o dígito; hasta 64 caracteres.",
           invalidName: "Nombre de perfil no válido",
      -    cloneFromDefault: "Clonar configuración del perfil predeterminado",
      +    cloneFrom: "Clonar desde el perfil",
      +    cloneFromNone: "Ninguno (vacío)",
           allProfiles: "Perfiles",
           noProfiles: "No se encontraron perfiles.",
           defaultBadge: "predeterminado",
    • web/src/i18n/fr.ts
      @@ -287,7 +287,8 @@ export const fr: Translations = {
           nameRule:
             "Lettres minuscules, chiffres, _ et - uniquement ; doit commencer par une lettre ou un chiffre ; jusqu'à 64 caractères.",
           invalidName: "Nom de profil invalide",
      -    cloneFromDefault: "Cloner la configuration du profil par défaut",
      +    cloneFrom: "Cloner depuis le profil",
      +    cloneFromNone: "Aucun (vide)",
           allProfiles: "Profils",
           noProfiles: "Aucun profil trouvé.",
           defaultBadge: "défaut",
    • web/src/i18n/ga.ts
      @@ -294,8 +294,8 @@ export const ga: Translations = {
           nameRequired: "Tá ainm riachtanach",
           nameRule:
             "Litreacha cás íochtair, digití, _ agus - amháin; caithfidh tús a chur le litir nó digit; suas le 64 carachtar.",
      -    invalidName: "Ainm próifíle neamhbhailí",
      -    cloneFromDefault: "Clónáil cumraíocht ón bpróifíl réamhshocraithe",
      +    invalidName: "Ainm próifíle neamhbhailí",    cloneFrom: "Clónáil cumraíocht ón bpróifíl",
      +    cloneFromNone: "Dada (folamh)",
           allProfiles: "Próifílí",
           noProfiles: "Níor aimsíodh próifílí.",
           defaultBadge: "réamhshocraithe",
    • web/src/i18n/hu.ts
      @@ -286,8 +286,8 @@ export const hu: Translations = {
           nameRequired: "A név kötelező",
           nameRule:
             "Csak kisbetűk, számjegyek, _ és - karakterek; betűvel vagy számjeggyel kell kezdődnie; legfeljebb 64 karakter.",
      -    invalidName: "Érvénytelen profilnév",
      -    cloneFromDefault: "Konfiguráció klónozása az alapértelmezett profilból",
      +    invalidName: "Érvénytelen profilnév",    cloneFrom: "Konfiguráció klónozása ebből a profilból",
      +    cloneFromNone: "Nincs (üres)",
           allProfiles: "Profilok",
           noProfiles: "Nem található profil.",
           defaultBadge: "alapértelmezett",
    • web/src/i18n/it.ts
      @@ -286,8 +286,8 @@ export const it: Translations = {
           nameRequired: "Il nome è obbligatorio",
           nameRule:
             "Solo lettere minuscole, cifre, _ e -; deve iniziare con una lettera o cifra; fino a 64 caratteri.",
      -    invalidName: "Nome del profilo non valido",
      -    cloneFromDefault: "Clona la configurazione dal profilo predefinito",
      +    invalidName: "Nome del profilo non valido",    cloneFrom: "Clona configurazione dal profilo",
      +    cloneFromNone: "Nessuno (vuoto)",
           allProfiles: "Profili",
           noProfiles: "Nessun profilo trovato.",
           defaultBadge: "predefinito",
    • web/src/i18n/ja.ts
      @@ -285,8 +285,8 @@ export const ja: Translations = {
           nameRequired: "名前は必須です",
           nameRule:
             "小文字、数字、_ および - のみ使用可能。最初は文字または数字で始める必要があります。最大 64 文字。",
      -    invalidName: "無効なプロファイル名",
      -    cloneFromDefault: "デフォルトプロファイルから設定を複製",
      +    invalidName: "無効なプロファイル名",    cloneFrom: "プロファイルから複製",
      +    cloneFromNone: "なし(空)",
           allProfiles: "プロファイル",
           noProfiles: "プロファイルが見つかりません。",
           defaultBadge: "デフォルト",
    • web/src/i18n/ko.ts
      @@ -285,8 +285,8 @@ export const ko: Translations = {
           nameRequired: "이름은 필수입니다",
           nameRule:
             "소문자, 숫자, _ 및 - 만 사용 가능합니다. 문자나 숫자로 시작해야 하며 최대 64자입니다.",
      -    invalidName: "잘못된 프로필 이름입니다",
      -    cloneFromDefault: "기본 프로필에서 설정 복제",
      +    invalidName: "잘못된 프로필 이름입니다",    cloneFrom: "프로필에서 복제",
      +    cloneFromNone: "없음 (빈 상태)",
           allProfiles: "프로필",
           noProfiles: "프로필을 찾을 수 없습니다.",
           defaultBadge: "기본",
    • web/src/i18n/pt.ts
      @@ -287,7 +287,8 @@ export const pt: Translations = {
           nameRule:
             "Apenas letras minúsculas, dígitos, _ e -; deve começar com letra ou dígito; até 64 caracteres.",
           invalidName: "Nome de perfil inválido",
      -    cloneFromDefault: "Clonar configuração do perfil predefinido",
      +    cloneFrom: "Clonar a partir do perfil",
      +    cloneFromNone: "Nenhum (vazio)",
           allProfiles: "Perfis",
           noProfiles: "Não foram encontrados perfis.",
           defaultBadge: "predefinido",
    • web/src/i18n/ru.ts
      @@ -286,8 +286,8 @@ export const ru: Translations = {
           nameRequired: "Имя обязательно",
           nameRule:
             "Только строчные буквы, цифры, _ и -; должно начинаться с буквы или цифры; до 64 символов.",
      -    invalidName: "Недопустимое имя профиля",
      -    cloneFromDefault: "Клонировать конфигурацию из профиля по умолчанию",
      +    invalidName: "Недопустимое имя профиля",    cloneFrom: "Клонировать конфигурацию из профиля",
      +    cloneFromNone: "Нет (пусто)",
           allProfiles: "Профили",
           noProfiles: "Профили не найдены.",
           defaultBadge: "по умолчанию",
    • web/src/i18n/tr.ts
      @@ -286,8 +286,8 @@ export const tr: Translations = {
           nameRequired: "Ad gereklidir",
           nameRule:
             "Yalnızca küçük harfler, rakamlar, _ ve - kullanılabilir; harf veya rakamla başlamalı; en fazla 64 karakter.",
      -    invalidName: "Geçersiz profil adı",
      -    cloneFromDefault: "Varsayılan profilden yapılandırmayı klonla",
      +    invalidName: "Geçersiz profil adı",    cloneFrom: "Profilden yapılandırmayı klonla",
      +    cloneFromNone: "Hiçbiri (boş)",
           allProfiles: "Profiller",
           noProfiles: "Profil bulunamadı.",
           defaultBadge: "varsayılan",
    • web/src/i18n/types.ts
      @@ -354,7 +354,8 @@ export interface Translations {
           nameRequired: string;
           nameRule: string;
           invalidName: string;
      -    cloneFromDefault: string;
      +    cloneFrom: string;
      +    cloneFromNone: string;
           allProfiles: string;
           noProfiles: string;
           defaultBadge: string;
    • web/src/i18n/uk.ts
      @@ -287,7 +287,8 @@ export const uk: Translations = {
           nameRule:
             "Лише малі літери, цифри, _ та -; має починатися з літери або цифри; до 64 символів.",
           invalidName: "Недопустима назва профілю",
      -    cloneFromDefault: "Клонувати конфігурацію з профілю за замовчуванням",
      +    cloneFrom: "Клонувати з профілю",
      +    cloneFromNone: "Жоден (порожній)",
           allProfiles: "Профілі",
           noProfiles: "Профілів не знайдено.",
           defaultBadge: "за замовчуванням",
    • web/src/i18n/zh-hant.ts
      @@ -285,8 +285,8 @@ export const zhHant: Translations = {
           nameRequired: "名稱為必填",
           nameRule:
             "僅允許小寫字母、數字、底線及連字號;首字必須為字母或數字;最多 64 個字元。",
      -    invalidName: "設定檔名稱無效",
      -    cloneFromDefault: "從預設設定檔複製設定",
      +    invalidName: "設定檔名稱無效",    cloneFrom: "從設定檔複製",
      +    cloneFromNone: "無(空白)",
           allProfiles: "設定檔",
           noProfiles: "找不到設定檔。",
           defaultBadge: "預設",
    • web/src/i18n/zh.ts
      @@ -282,8 +282,8 @@ export const zh: Translations = {
           nameRequired: "名称必填",
           nameRule:
             "仅允许小写字母、数字、下划线和短横线;首字符必须是字母或数字;最多 64 个字符。",
      -    invalidName: "多Agent配置名称非法",
      -    cloneFromDefault: "从默认多Agent配置克隆配置",
      +    invalidName: "多Agent配置名称非法",    cloneFrom: "从配置文件克隆",
      +    cloneFromNone: "无(空白)",
           allProfiles: "多Agent配置列表",
           noProfiles: "暂无多Agent配置。",
           defaultBadge: "默认",
    • web/src/lib/api.ts
      @@ -552,7 +552,8 @@ export const api = {
           }),
         createProfile: (body: {
           name: string;
      -    clone_from_default: boolean;
      +    clone_from?: string | null;
      +    clone_from_default?: boolean;
           clone_all?: boolean;
           no_skills?: boolean;
           description?: string;
    • web/src/pages/ProfileBuilderPage.tsx
      @@ -220,7 +220,7 @@ export default function ProfileBuilderPage() {
           try {
             const res = await api.createProfile({
               name: n,
      -        clone_from_default: false,
      +        clone_from: null,
               description: description.trim() || undefined,
               provider: pickedModel?.provider,
               model: pickedModel?.model,
    • web/src/pages/ProfilesPage.tsx
      @@ -35,11 +35,11 @@ import { Badge } from "@nous-research/ui/ui/components/badge";
       import { Button } from "@nous-research/ui/ui/components/button";
       import { Input } from "@nous-research/ui/ui/components/input";
       import { Label } from "@nous-research/ui/ui/components/label";
      -import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
       import {
         Select,
         SelectOption,
       } from "@nous-research/ui/ui/components/select";
      +import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
       import { useI18n } from "@/i18n";
       import { usePageHeader } from "@/contexts/usePageHeader";
       import { cn, themedBody } from "@/lib/utils";
      @@ -312,7 +312,7 @@ export default function ProfilesPage() {
         // Create modal
         const [createModalOpen, setCreateModalOpen] = useState(false);
         const [newName, setNewName] = useState("");
      -  const [cloneFromDefault, setCloneFromDefault] = useState(true);
      +  const [cloneFrom, setCloneFrom] = useState<string | null>("default");
         const [cloneAll, setCloneAll] = useState(false);
         const [noSkills, setNoSkills] = useState(false);
         const [newDescription, setNewDescription] = useState("");
      @@ -429,7 +429,7 @@ export default function ProfilesPage() {
           }
           setCreating(true);
           try {
      -      const cloning = cloneAll || cloneFromDefault;
      +      const cloning = cloneFrom !== null;
             const picked = modelChoice
               ? modelChoices?.find(
                   (c) => `${c.provider}\u0000${c.model}` === modelChoice,
      @@ -437,8 +437,8 @@ export default function ProfilesPage() {
               : undefined;
             const res = await api.createProfile({
               name,
      -        clone_from_default: cloneAll ? false : cloneFromDefault,
      -        clone_all: cloneAll,
      +        clone_from: cloneFrom,
      +        clone_all: cloning && cloneAll,
               no_skills: cloning ? false : noSkills,
               description: newDescription.trim() || undefined,
               provider: picked?.provider,
      @@ -455,7 +455,7 @@ export default function ProfilesPage() {
             setNewDescription("");
             setNoSkills(false);
             setCloneAll(false);
      -      setCloneFromDefault(true);
      +      setCloneFrom("default");
             setModelChoice("");
             setCreateModalOpen(false);
             load();
      @@ -772,7 +772,7 @@ export default function ProfilesPage() {
           };
         }, [setEnd, t.common.create, loading, navigate]);
       
      -  const cloning = cloneAll || cloneFromDefault;
      +  const cloning = cloneFrom !== null;
       
         if (loading) {
           return (
      @@ -862,6 +862,26 @@ export default function ProfilesPage() {
                       </p>
                     </div>
       
      +              <div className="grid gap-2">
      +                <Label htmlFor="clone-from">{t.profiles.cloneFrom}</Label>
      +                <Select
      +                  id="clone-from"
      +                  value={cloneFrom ?? ""}
      +                  onValueChange={(v) => {
      +                    const next = v || null;
      +                    setCloneFrom(next);
      +                    if (next === null) setCloneAll(false);
      +                  }}
      +                >
      +                  <SelectOption value="">{t.profiles.cloneFromNone}</SelectOption>
      +                  {profiles.map((profile) => (
      +                    <SelectOption key={profile.name} value={profile.name}>
      +                      {profile.name}
      +                    </SelectOption>
      +                  ))}
      +                </Select>
      +              </div>
      +
                     <div className="grid gap-2">
                       <Label htmlFor="profile-description">
                         {L.descriptionOptional}
      @@ -909,33 +929,19 @@ export default function ProfilesPage() {
                         {L.advancedOptions}
                       </legend>
       
      -                <div className="flex items-center gap-2.5">
      -                  <Checkbox
      -                    checked={cloneFromDefault}
      -                    id="clone-from-default"
      -                    disabled={cloneAll}
      -                    onCheckedChange={(checked) =>
      -                      setCloneFromDefault(checked === true)
      -                    }
      -                  />
      -
      -                  <Label
      -                    className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
      -                    htmlFor="clone-from-default"
      -                  >
      -                    {t.profiles.cloneFromDefault}
      -                  </Label>
      -                </div>
      -
                       <div className="flex items-center gap-2.5">
                         <Checkbox
                           checked={cloneAll}
      +                    disabled={!cloning}
                           id="clone-all"
                           onCheckedChange={(checked) => setCloneAll(checked === true)}
                         />
       
                         <Label
      -                    className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
      +                    className={cn(
      +                      "font-mondwest normal-case tracking-normal text-sm cursor-pointer",
      +                      !cloning && "opacity-50",
      +                    )}
                           htmlFor="clone-all"
                         >
                           {L.cloneAll}
    • website/docs/reference/cli-commands.md
      @@ -1396,7 +1396,7 @@ Manage profiles — multiple isolated Hermes instances, each with its own config
       |------------|-------------|
       | `list` | List all profiles. |
       | `use <name>` | Set a sticky default profile. |
      -| `create <name> [--clone] [--clone-all] [--clone-from <source>] [--no-alias]` | Create a new profile. `--clone` copies config, `.env`, and `SOUL.md` from the active profile. `--clone-all` copies all state. `--clone-from` specifies a source profile. |
      +| `create <name> [--clone] [--clone-all] [--clone-from <source>] [--no-alias]` | Create a new profile. `--clone` copies config, `.env`, `SOUL.md`, and skills from the active profile. `--clone-all` copies all state. `--clone-from` specifies a source profile and implies config clone unless paired with `--clone-all`. |
       | `delete <name> [-y]` | Delete a profile. |
       | `show <name>` | Show profile details (home directory, config, etc.). |
       | `alias <name> [--remove] [--name NAME]` | Manage wrapper scripts for quick profile access. |
    • website/docs/reference/faq.md
      @@ -634,7 +634,7 @@ No. Each messaging platform (Telegram, Discord, etc.) requires exclusive access
       
       ### Do profiles share memory or sessions?
       
      -No. Each profile has its own memory store, session database, and skills directory. They are completely isolated. If you want to start a new profile with existing memories and sessions, use `hermes profile create newname --clone-all` to copy everything from the current profile.
      +No. Each profile has its own memory store, session database, and skills directory. They are completely isolated. If you want to start a new profile with existing memories and sessions, use `hermes profile create newname --clone-all` to copy everything from the current profile, or add `--clone-from <profile>` to copy from a specific source profile.
       
       ### What happens when I run `hermes update`?
       
    • website/docs/reference/profile-commands.md
      @@ -80,12 +80,12 @@ Creates a new profile.
       | Argument / Option | Description |
       |-------------------|-------------|
       | `<name>` | Name for the new profile. Must be a valid directory name (alphanumeric, hyphens, underscores). |
      -| `--clone` | Copy `config.yaml`, `.env`, and `SOUL.md` from the current profile. |
      +| `--clone` | Copy `config.yaml`, `.env`, `SOUL.md`, and skills from the current profile. |
       | `--clone-all` | Copy everything (config, memories, skills, cron, plugins) from the current profile. Excludes per-profile history: sessions, `state.db`, backups, state-snapshots, checkpoints. |
      -| `--clone-from <profile>` | Clone from a specific profile instead of the current one. Used with `--clone` or `--clone-all`. |
      +| `--clone-from <profile>` | Clone config/skills/SOUL from a specific profile instead of the current one. Implies `--clone` unless paired with `--clone-all`. |
       | `--no-alias` | Skip wrapper script creation. |
       | `--description "<text>"` | One- or two-sentence description of what this profile is good at. Used by the kanban orchestrator to route tasks based on role instead of profile name alone. Skip and add later via `hermes profile describe`. Persisted in `<profile_dir>/profile.yaml`. |
      -| `--no-skills` | Create an **empty** profile with zero bundled skills enabled. Writes a `.no-bundled-skills` marker into the profile so future `hermes update` runs won't re-seed the bundled set, and refuses to combine with `--clone` / `--clone-all` (which would copy skills in anyway). Useful for narrow orchestrator profiles or sandbox profiles that should not inherit the full skill catalog. To toggle this on an already-created profile (including the default `~/.hermes`), use `hermes skills opt-out` / `hermes skills opt-in`. |
      +| `--no-skills` | Create an **empty** profile with zero bundled skills enabled. Writes a `.no-bundled-skills` marker into the profile so future `hermes update` runs won't re-seed the bundled set, and refuses to combine with `--clone`, `--clone-from`, or `--clone-all` (which would copy skills in anyway). Useful for narrow orchestrator profiles or sandbox profiles that should not inherit the full skill catalog. To toggle this on an already-created profile (including the default `~/.hermes`), use `hermes skills opt-out` / `hermes skills opt-in`. |
       
       Creating a profile does **not** make that profile directory the default project/workspace directory for terminal commands. If you want a profile to start in a specific project, set `terminal.cwd` in that profile's `config.yaml`.
       
      @@ -102,7 +102,10 @@ hermes profile create work --clone
       hermes profile create backup --clone-all
       
       # Clone config from a specific profile
      -hermes profile create work2 --clone --clone-from work
      +hermes profile create work2 --clone-from work
      +
      +# Clone everything from a specific profile
      +hermes profile create work2-backup --clone-from work --clone-all
       ```
       
       ## `hermes profile describe`
    • website/docs/user-guide/profiles.md
      @@ -50,7 +50,7 @@ You can also set or auto-generate the description later with `hermes profile des
       hermes profile create work --clone
       ```
       
      -Copies your current profile's `config.yaml`, `.env`, and `SOUL.md` into the new profile. Same API keys and model, but fresh sessions and memory. Edit `~/.hermes/profiles/work/.env` for different API keys, or `~/.hermes/profiles/work/SOUL.md` for a different personality.
      +Copies your current profile's `config.yaml`, `.env`, `SOUL.md`, and skills into the new profile. Same API keys, model, and capabilities, but fresh sessions and memory. Edit `~/.hermes/profiles/work/.env` for different API keys, or `~/.hermes/profiles/work/SOUL.md` for a different personality.
       
       ### Clone everything (`--clone-all`)
       
      @@ -63,11 +63,17 @@ Copies **everything** — config, API keys, personality, all memories, skills, c
       ### Clone from a specific profile
       
       ```bash
      -hermes profile create work --clone --clone-from coder
      +hermes profile create work --clone-from coder
      +```
      +
      +`--clone-from <source>` selects the source profile directly and implies a config/skills/SOUL clone. Combine it with `--clone-all` when you want a full copy of that source profile:
      +
      +```bash
      +hermes profile create work-backup --clone-from coder --clone-all
       ```
       
       :::tip Honcho memory + profiles
      -When Honcho is enabled, `--clone` automatically creates a dedicated AI peer for the new profile while sharing the same user workspace. Each profile builds its own observations and identity. See [Honcho -- Multi-agent / Profiles](./features/memory-providers.md#honcho) for details.
      +When Honcho is enabled, clone operations automatically create a dedicated AI peer for the new profile while sharing the same user workspace. Each profile builds its own observations and identity. See [Honcho -- Multi-agent / Profiles](./features/memory-providers.md#honcho) for details.
       :::
       
       ## Using profiles
    • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/cli-commands.md
      @@ -1175,7 +1175,7 @@ hermes profile <subcommand>
       |------------|-------------|
       | `list` | 列出所有 profile。 |
       | `use <name>` | 设置粘性默认 profile。 |
      -| `create <name> [--clone] [--clone-all] [--clone-from <source>] [--no-alias]` | 创建新 profile。`--clone` 从活跃 profile 复制 config、`.env` 和 `SOUL.md`。`--clone-all` 复制所有状态。`--clone-from` 指定源 profile。 |
      +| `create <name> [--clone] [--clone-all] [--clone-from <source>] [--no-alias]` | 创建新 profile。`--clone` 从活跃 profile 复制 config、`.env`、`SOUL.md` 和 skills。`--clone-all` 复制所有状态。`--clone-from` 指定源 profile,除非与 `--clone-all` 配合使用,否则会隐含 config 克隆。 |
       | `delete <name> [-y]` | 删除 profile。 |
       | `show <name>` | 显示 profile 详情(主目录、config 等)。 |
       | `alias <name> [--remove] [--name NAME]` | 管理快速访问 profile 的包装脚本。 |
    • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/faq.md
      @@ -626,7 +626,7 @@ Profiles 是构建在 `HERMES_HOME` 之上的托管层。您*可以*在每次命
       
       ### Profiles 共享记忆或会话吗?
       
      -不共享。每个 profile 都有自己独立的记忆存储、会话数据库和技能目录,完全隔离。如果您想用现有的记忆和会话创建新 profile,请使用 `hermes profile create newname --clone-all` 从当前 profile 复制所有内容。
      +不共享。每个 profile 都有自己独立的记忆存储、会话数据库和技能目录,完全隔离。如果您想用现有的记忆和会话创建新 profile,请使用 `hermes profile create newname --clone-all` 从当前 profile 复制所有内容,或添加 `--clone-from <profile>` 从指定源 profile 复制。
       
       ### 运行 `hermes update` 时会发生什么?
       
    • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/profile-commands.md
      @@ -79,12 +79,12 @@ hermes profile create <name> [options]
       | 参数 / 选项 | 描述 |
       |-------------------|-------------|
       | `<name>` | 新 profile 的名称。必须是合法的目录名(字母数字、连字符、下划线)。 |
      -| `--clone` | 从当前 profile 复制 `config.yaml`、`.env` 和 `SOUL.md`。 |
      -| `--clone-all` | 从当前 profile 复制所有内容(config、memories、skills、sessions、state)。 |
      -| `--clone-from <profile>` | 从指定 profile 克隆,而非当前 profile。与 `--clone` 或 `--clone-all` 配合使用。 |
      +| `--clone` | 从当前 profile 复制 `config.yaml`、`.env`、`SOUL.md` 和 skills。 |
      +| `--clone-all` | 从当前 profile 复制所有内容(config、memories、skills、cron、plugins)。会排除每个 profile 自己的历史数据:sessions、`state.db`、backups、state-snapshots、checkpoints。 |
      +| `--clone-from <profile>` | 从指定 profile 克隆 config/skills/SOUL,而非当前 profile。除非与 `--clone-all` 配合使用,否则会隐含 `--clone`。 |
       | `--no-alias` | 跳过 wrapper 脚本创建。 |
       | `--description "<text>"` | 一到两句话描述该 profile 的用途。供 kanban 编排器根据角色而非仅凭 profile 名称来路由任务。可跳过,稍后通过 `hermes profile describe` 添加。持久化保存在 `<profile_dir>/profile.yaml` 中。 |
      -| `--no-skills` | 创建一个**空** profile,不启用任何内置 skill。会在 profile 目录中写入 `.no-skills` 标记,使后续 `hermes update` 不再重新植入内置 skill 集,且拒绝与 `--clone` / `--clone-all` 组合使用(因为后者会复制 skill)。适用于不应继承完整 skill 目录的窄化编排器 profile 或沙箱 profile。 |
      +| `--no-skills` | 创建一个**空** profile,不启用任何内置 skill。会在 profile 目录中写入 `.no-bundled-skills` 标记,使后续 `hermes update` 不再重新植入内置 skill 集,且拒绝与 `--clone`、`--clone-from` 或 `--clone-all` 组合使用(因为这些选项会复制 skill)。适用于不应继承完整 skill 目录的窄化编排器 profile 或沙箱 profile。 |
       
       创建 profile **不会**将该 profile 目录设为终端命令的默认项目/工作目录。如需让某个 profile 从特定项目目录启动,请在该 profile 的 `config.yaml` 中设置 `terminal.cwd`。
       
      @@ -101,7 +101,10 @@ hermes profile create work --clone
       hermes profile create backup --clone-all
       
       # 从指定 profile 克隆 config
      -hermes profile create work2 --clone --clone-from work
      +hermes profile create work2 --clone-from work
      +
      +# 从指定 profile 克隆所有内容
      +hermes profile create work2-backup --clone-from work --clone-all
       ```
       
       ## `hermes profile describe`
    • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/profiles.md
      @@ -46,7 +46,7 @@ hermes profile create researcher --description "Reads source code and external d
       hermes profile create work --clone
       ```
       
      -将当前 profile 的 `config.yaml`、`.env` 和 `SOUL.md` 复制到新 profile。API 密钥和模型相同,但会话和记忆是全新的。编辑 `~/.hermes/profiles/work/.env` 可使用不同的 API 密钥,编辑 `~/.hermes/profiles/work/SOUL.md` 可设置不同的人格。
      +将当前 profile 的 `config.yaml`、`.env`、`SOUL.md` 和 skills 复制到新 profile。API 密钥、模型和能力相同,但会话和记忆是全新的。编辑 `~/.hermes/profiles/work/.env` 可使用不同的 API 密钥,编辑 `~/.hermes/profiles/work/SOUL.md` 可设置不同的人格。
       
       ### 克隆全部内容(`--clone-all`)
       
      @@ -54,16 +54,22 @@ hermes profile create work --clone
       hermes profile create backup --clone-all
       ```
       
      -复制**所有内容**——配置、API 密钥、人格、所有记忆、完整会话历史、技能、cron 任务、插件。完整快照。适用于备份或 fork 已有上下文的 agent。
      +复制**所有内容**——配置、API 密钥、人格、记忆、技能、cron 任务、插件。会排除每个 profile 自己的历史数据(会话历史、`state.db`、`backups/`、`state-snapshots/`、`checkpoints/`),这些数据属于源 profile 且可能达到数十 GB。若要包含历史的完整备份,请使用 `hermes profile export` 或 `hermes backup`。
       
       ### 从指定 profile 克隆
       
       ```bash
      -hermes profile create work --clone --clone-from coder
      +hermes profile create work --clone-from coder
      +```
      +
      +`--clone-from <source>` 会直接选择源 profile,并隐含执行 config/skills/SOUL 克隆。若要完整复制该源 profile,请与 `--clone-all` 组合使用:
      +
      +```bash
      +hermes profile create work-backup --clone-from coder --clone-all
       ```
       
       :::tip Honcho 记忆 + profiles
      -启用 Honcho 后,`--clone` 会自动为新 profile 创建专属 AI 对等体,同时共享同一用户工作区。每个 profile 构建各自的观察记录和身份标识。详见 [Honcho——多 agent / Profiles](./features/memory-providers.md#honcho)。
      +启用 Honcho 后,克隆操作会自动为新 profile 创建专属 AI 对等体,同时共享同一用户工作区。每个 profile 构建各自的观察记录和身份标识。详见 [Honcho——多 agent / Profiles](./features/memory-providers.md#honcho)。
       :::
       
       ## 使用 profile

1 PR closed without merging

From the close comments: merged via another change 1

  • #45558 feat(dashboard): clone new profile from any profile (dropdown instead of checkbox) · merged via another change → #45630
Closed issues · 1
  • Closed#23746[Feature]: Dashboard — clone new profile from any profile (dropdown instead of checkbox)
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
hermes cli
Merged PRs
#45630
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
13 Jun 2026

For your agent

The same facts, typed.