[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"url-settings":80,"blog-\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk":589,"blog-author-\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk":1082},{"id":4,"extension":5,"footer":6,"header":66,"meta":77,"stem":78,"__hash__":79},"navigation\u002Fdata\u002Fshared\u002Fnavigation.yml","yml",{"brand":7,"columns":10,"legal":56},{"name":8,"tagline":9},"Pieces","The memory layer for modern work.",[11,26,41],{"title":12,"links":13},"Product",[14,17,21,24],{"label":15,"href":16},"Pieces Desktop","\u002Fdownloads",{"label":18,"href":19,"external":20},"Pieces MCP","url:docs.mcp.overview",true,{"label":22,"href":23,"external":20},"Pieces APIs","url:docs.api",{"label":25,"href":16},"Downloads",{"title":27,"links":28},"Resources",[29,32,35,38],{"label":30,"href":31,"external":20},"Documentation","url:docs.home",{"label":33,"href":34},"Blog","\u002Fblog",{"label":36,"href":37},"Changelog","\u002Fchangelog",{"label":39,"href":40,"external":20},"GitHub","url:github.org",{"title":42,"links":43},"Company",[44,47,50,53],{"label":45,"href":46},"About","\u002Fabout",{"label":48,"href":49},"Enterprise","\u002Fenterprise",{"label":51,"href":52,"external":20},"Discord","url:social.discord",{"label":54,"href":55,"external":20},"X \u002F Twitter","url:social.x",[57,60,63],{"label":58,"href":59,"external":20},"Privacy Policy","url:legal.privacyPolicy",{"label":61,"href":62,"external":20},"Refund Policy","url:legal.refundPolicy",{"label":64,"href":65,"external":20},"Terms of Service","url:legal.terms",{"links":67,"signIn":68,"contact":71,"cta":74},[],{"label":69,"href":70},"Sign in","url:portal.home",{"label":72,"href":73},"Contact sales","url:site.contact",{"label":75,"href":76},"Download","url:routes.downloads",{},"data\u002Fshared\u002Fnavigation","Ia8tCWWqcGvuaIro8jwZ3HH-MwI66yqJpWshASJdYQ0",{"id":81,"extension":5,"links":82,"meta":586,"stem":587,"__hash__":588},"urlSettings\u002Fdata\u002Fshared\u002Furls.yml",[83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191,195,199,203,207,211,215,219,223,227,231,235,238,242,246,249,253,257,261,265,269,273,277,281,285,289,293,297,301,305,309,313,317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,396,400,404,408,412,416,420,423,426,429,432,436,440,444,448,452,456,460,464,468,472,476,480,484,488,492,495,499,503,507,511,515,519,523,527,531,534,538,542,546,550,553,557,561,565,568,571,575,579,582],{"key":84,"label":85,"href":86},"downloads.desktop","Desktop download page","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdownload",{"key":88,"label":89,"href":90},"downloads.macOS.dmgArm64","macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg-arm64\u002Fdownload",{"key":92,"label":93,"href":94},"downloads.macOS.dmgIntel","macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg\u002Fdownload",{"key":96,"label":97,"href":98},"downloads.macOS.pkg","macOS PKG","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg\u002Fdownload",{"key":100,"label":101,"href":102},"downloads.windows.appinstaller","Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fpieces_for_x.appinstaller",{"key":104,"label":105,"href":106},"downloads.windows.exe","Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fwindows-exe\u002Fdownload",{"key":108,"label":109,"href":110},"downloads.windows.suiteManager","Windows Suite Manager","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_suite_windows\u002Fappinstaller\u002Fdownload",{"key":112,"label":113,"href":114},"downloads.linux.flatpakRepo","Linux Flatpak repository","https:\u002F\u002Fbuilds.pieces.app\u002Fpieces-flatpak-repo\u002Fpieces-flatpak.flatpakrepo",{"key":116,"label":117,"href":118},"downloads.linux.snapDesktop","Linux Snap Desktop","https:\u002F\u002Fsnapcraft.io\u002Fpieces-for-developers",{"key":120,"label":121,"href":122},"downloads.linux.snapPiecesOS","Linux Snap PiecesOS","https:\u002F\u002Fsnapcraft.io\u002Fpieces-os",{"key":124,"label":125,"href":126},"downloads.piecesOS.macOS.dmgArm64","PiecesOS macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg-arm64\u002Fdownload",{"key":128,"label":129,"href":130},"downloads.piecesOS.macOS.dmgIntel","PiecesOS macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg\u002Fdownload",{"key":132,"label":133,"href":134},"downloads.piecesOS.windows.appinstaller","PiecesOS Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fos_server.appinstaller",{"key":136,"label":137,"href":138},"downloads.piecesOS.windows.exe","PiecesOS Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fwindows-exe\u002Fdownload",{"key":140,"label":141,"href":142},"downloads.guides.macOS","macOS installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fmacos-installation-guide",{"key":144,"label":145,"href":146},"downloads.guides.windows","Windows installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fwindows-installation-guide",{"key":148,"label":149,"href":150},"downloads.guides.linux","Linux installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Flinux-installation-guide",{"key":152,"label":153,"href":154},"downloads.guides.piecesOS","PiecesOS manual installation","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fmanual-installation",{"key":156,"label":157,"href":158},"extensions.chrome","Chrome extension","https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fpieces-save-code-snippets\u002Figbgibhbfonhmjlechmeefimncpekepm",{"key":160,"label":161,"href":162},"extensions.firefox","Firefox add-on","https:\u002F\u002Faddons.mozilla.org\u002Fen-US\u002Ffirefox\u002Faddon\u002Fpieces-save-code-from-the-web\u002F",{"key":164,"label":165,"href":166},"extensions.edge","Edge add-on","https:\u002F\u002Fmicrosoftedge.microsoft.com\u002Faddons\u002Fdetail\u002Fpieces-save-code-snippet\u002Fhglfimcdgonaeeobjckfdabcldfidmim",{"key":168,"label":169,"href":170},"extensions.vscode","VS Code extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.pieces-vscode",{"key":172,"label":173,"href":174},"extensions.visualStudio","Visual Studio extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.PiecesVisualStudio",{"key":176,"label":177,"href":178},"extensions.jetbrains","JetBrains plugin","https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F17328-pieces--save-search-share--reuse-code-snippets",{"key":180,"label":181,"href":182},"extensions.obsidian","Obsidian plugin","https:\u002F\u002Fobsidian.md\u002Fplugins?id=pieces-for-developers",{"key":184,"label":185,"href":186},"extensions.sublime","Sublime package","https:\u002F\u002Fpackagecontrol.io\u002Fpackages\u002FPieces",{"key":188,"label":189,"href":190},"extensions.neovim","Neovim plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neo_vim",{"key":192,"label":193,"href":194},"extensions.jupyterlab","JupyterLab plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fjupyterlab-pieces",{"key":196,"label":197,"href":198},"extensions.cli","Pieces CLI","https:\u002F\u002Fpypi.org\u002Fproject\u002Fpieces-cli\u002F",{"key":200,"label":201,"href":202},"docs.home","Documentation home","https:\u002F\u002Fdocs.pieces.app",{"key":204,"label":205,"href":206},"docs.getStarted","Get started docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces",{"key":208,"label":209,"href":210},"docs.api","API docs","https:\u002F\u002Fdocs.pieces.app\u002Fapi",{"key":212,"label":213,"href":214},"docs.desktop.overview","Desktop overview","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop",{"key":216,"label":217,"href":218},"docs.desktop.onboarding","Desktop onboarding","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fonboarding",{"key":220,"label":221,"href":222},"docs.desktop.timeline","Desktop timeline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Ftimeline",{"key":224,"label":225,"href":226},"docs.desktop.summaries","Desktop summaries docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fsingle-click-summaries",{"key":228,"label":229,"href":230},"docs.desktop.search","Desktop conversational search docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fconversational-search",{"key":232,"label":233,"href":234},"docs.desktop.drive","Desktop drive docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdrive",{"key":236,"label":237,"href":86},"docs.desktop.download","Desktop download docs",{"key":239,"label":240,"href":241},"docs.piecesOS.overview","PiecesOS overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies",{"key":243,"label":244,"href":245},"docs.piecesOS.details","PiecesOS details docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os",{"key":247,"label":248,"href":154},"docs.piecesOS.install","PiecesOS install docs",{"key":250,"label":251,"href":252},"docs.piecesOS.quickMenu","PiecesOS quick menu docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fquick-menu",{"key":254,"label":255,"href":256},"docs.piecesOS.storage","On-device storage docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fon-device-storage",{"key":258,"label":259,"href":260},"docs.piecesOS.troubleshooting","PiecesOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Ftroubleshooting",{"key":262,"label":263,"href":264},"docs.mcp.overview","MCP overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp",{"key":266,"label":267,"href":268},"docs.mcp.cursor","MCP Cursor docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcursor",{"key":270,"label":271,"href":272},"docs.mcp.vscode","MCP VS Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fvs-code",{"key":274,"label":275,"href":276},"docs.mcp.claudeDesktop","MCP Claude Desktop docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-desktop",{"key":278,"label":279,"href":280},"docs.mcp.claudeCode","MCP Claude Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-code",{"key":282,"label":283,"href":284},"docs.mcp.claudeCowork","MCP Claude Cowork docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-cowork",{"key":286,"label":287,"href":288},"docs.mcp.githubCopilot","MCP GitHub Copilot docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgithub-copilot",{"key":290,"label":291,"href":292},"docs.mcp.goose","MCP Goose docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoose",{"key":294,"label":295,"href":296},"docs.mcp.windsurf","MCP Windsurf docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fwindsurf",{"key":298,"label":299,"href":300},"docs.mcp.zed","MCP Zed docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fzed",{"key":302,"label":303,"href":304},"docs.mcp.jetbrains","MCP JetBrains docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fjetbrains-ides",{"key":306,"label":307,"href":308},"docs.mcp.continueDev","MCP Continue docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcontinue-dev",{"key":310,"label":311,"href":312},"docs.mcp.cline","MCP Cline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcline",{"key":314,"label":315,"href":316},"docs.mcp.raycast","MCP Raycast docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fraycast",{"key":318,"label":319,"href":320},"docs.mcp.rovoDevCli","MCP Rovo Dev CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Frovo-dev-cli",{"key":322,"label":323,"href":324},"docs.mcp.openaiCodexCli","MCP OpenAI Codex CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenai-codex-cli",{"key":326,"label":327,"href":328},"docs.mcp.googleGeminiCli","MCP Google Gemini CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoogle-gemini-cli",{"key":330,"label":331,"href":332},"docs.mcp.amazonQ","MCP Amazon Q docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Famazon-q-developer",{"key":334,"label":335,"href":336},"docs.mcp.chatgptDev","MCP ChatGPT Developer Mode docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fchatgpt-developer-mode",{"key":338,"label":339,"href":340},"docs.mcp.openclaw","MCP OpenClaw docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenclaw",{"key":342,"label":343,"href":344},"docs.mcp.mcpRemote","MCP Remote docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fmcp-remote",{"key":346,"label":347,"href":348},"docs.mcp.ngrok","MCP ngrok docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fngrok-setup",{"key":350,"label":351,"href":352},"docs.troubleshooting.macOS","macOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fmacos",{"key":354,"label":355,"href":356},"docs.troubleshooting.windows","Windows troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fwindows",{"key":358,"label":359,"href":360},"docs.troubleshooting.linux","Linux troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Flinux",{"key":362,"label":363,"href":364},"docs.privacy","Privacy and security docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fprivacy-security-your-data",{"key":366,"label":367,"href":368},"docs.support","Support docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fsupport",{"key":370,"label":371,"href":372},"portal.home","Pieces portal","https:\u002F\u002Fportal.pieces.app",{"key":374,"label":375,"href":376},"site.home","Website home","https:\u002F\u002Fpieces.app",{"key":378,"label":379,"href":380},"site.about","About page","https:\u002F\u002Fpieces.app\u002Fabout",{"key":382,"label":383,"href":384},"site.features","Features page","https:\u002F\u002Fpieces.app\u002Ffeatures",{"key":386,"label":387,"href":388},"site.plugins","Plugins page","https:\u002F\u002Fpieces.app\u002Fplugins",{"key":390,"label":391,"href":392},"site.contact","Contact page","https:\u002F\u002Fpieces.app\u002Fcontact",{"key":394,"label":36,"href":395},"site.changelog","https:\u002F\u002Fpieces.app\u002Fchangelog",{"key":397,"label":398,"href":399},"site.news","News","https:\u002F\u002Fpieces.app\u002Fnews",{"key":401,"label":402,"href":403},"site.events","Community events","https:\u002F\u002Fpieces.app\u002Fcommunity\u002Fevents",{"key":405,"label":406,"href":407},"site.userStories","User stories","https:\u002F\u002Fpieces.app\u002Fuser-stories",{"key":409,"label":410,"href":411},"site.academy","Academy","https:\u002F\u002Fpieces.app\u002Flearn\u002Facademy",{"key":413,"label":414,"href":415},"site.support","Website support","https:\u002F\u002Fpieces.app\u002Fsupport",{"key":417,"label":418,"href":419},"site.standup","Standup","https:\u002F\u002Fpieces.app\u002Fstandup",{"key":421,"label":33,"href":422},"site.blog","https:\u002F\u002Fcode.pieces.app\u002Fblog",{"key":424,"label":51,"href":425},"social.discord","https:\u002F\u002Fdiscord.gg\u002Fgetpieces",{"key":427,"label":54,"href":428},"social.x","https:\u002F\u002Fx.com\u002Fgetpieces",{"key":430,"label":431,"href":428},"social.twitter","Twitter",{"key":433,"label":434,"href":435},"social.instagram","Instagram","https:\u002F\u002Fwww.instagram.com\u002Fgetpieces\u002F",{"key":437,"label":438,"href":439},"social.tiktok","TikTok","https:\u002F\u002Fwww.tiktok.com\u002F@getpieces",{"key":441,"label":442,"href":443},"social.linkedin","LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fgetpieces\u002F",{"key":445,"label":446,"href":447},"social.youtube","YouTube","https:\u002F\u002Fyoutube.com\u002F@getpieces",{"key":449,"label":450,"href":451},"github.org","GitHub organization","https:\u002F\u002Fgithub.com\u002Fpieces-app",{"key":453,"label":454,"href":455},"github.support","GitHub support","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport",{"key":457,"label":458,"href":459},"github.issues","GitHub issues","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fissues",{"key":461,"label":462,"href":463},"github.discussions","GitHub discussions","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fdiscussions",{"key":465,"label":466,"href":467},"github.documentation","GitHub documentation","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fdocumentation",{"key":469,"label":470,"href":471},"github.opensource","GitHub open source","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fopensource",{"key":473,"label":474,"href":475},"github.sdks.python","Python SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-python",{"key":477,"label":478,"href":479},"github.sdks.typescript","TypeScript SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-typescript",{"key":481,"label":482,"href":483},"github.sdks.dart","Dart SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-dart",{"key":485,"label":486,"href":487},"github.sdks.kotlin","Kotlin SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-kotlin",{"key":489,"label":490,"href":491},"github.plugins.obsidian","Obsidian plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fobsidian-pieces",{"key":493,"label":494,"href":194},"github.plugins.jupyterlab","JupyterLab plugin repository",{"key":496,"label":497,"href":498},"github.plugins.sublime","Sublime plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_sublime",{"key":500,"label":501,"href":502},"github.plugins.neovim","Neovim plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neovim",{"key":504,"label":505,"href":506},"github.cliAgent","CLI agent repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fcli-agent",{"key":508,"label":509,"href":510},"github.mcpDart","MCP Dart repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fmcp_dart",{"key":512,"label":513,"href":514},"github.awesomePieces","Awesome Pieces repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fawesome-pieces",{"key":516,"label":517,"href":518},"legal.privacyPolicy","Privacy policy","https:\u002F\u002Fpieces.app\u002Flegal\u002Fprivacy-policy",{"key":520,"label":521,"href":522},"legal.refundPolicy","Refund policy","https:\u002F\u002Fpieces.app\u002Flegal\u002Frefund-policy",{"key":524,"label":525,"href":526},"legal.terms","Terms","https:\u002F\u002Fpieces.app\u002Flegal\u002Fterms",{"key":528,"label":529,"href":530},"legal.security","Legal security","https:\u002F\u002Fpieces.app\u002Flegal\u002Fsecurity",{"key":532,"label":533,"href":447},"videos.youtubeChannel","YouTube channel",{"key":535,"label":536,"href":537},"videos.gettingStartedDesktop","Getting started desktop video","https:\u002F\u002Fyoutu.be\u002FdUr1lRM_TYk",{"key":539,"label":540,"href":541},"videos.snippetDiscovery","Snippet discovery video","https:\u002F\u002Fyoutu.be\u002FG6vb1USw-30",{"key":543,"label":544,"href":545},"sales.bookACall","Book a sales call","https:\u002F\u002Fcalendar.app.google\u002FWVUDtUfNy5Vst3sH7",{"key":547,"label":548,"href":549},"sales.enterprise","Enterprise form","https:\u002F\u002Fgetpieces.typeform.com\u002Fto\u002FaVQFTvpE",{"key":551,"label":552,"href":463},"sales.feedback","Feedback discussions",{"key":554,"label":555,"href":556},"sales.earlyAccess","Early access form","https:\u002F\u002Fgetpieces.typeform.com\u002Fearlyaccess",{"key":558,"label":559,"href":560},"sales.supportEmail","Support email","mailto:support@pieces.app",{"key":562,"label":563,"href":564},"routes.home","Home route","\u002F",{"key":566,"label":567,"href":46},"routes.about","About route",{"key":569,"label":570,"href":16},"routes.downloads","Downloads route",{"key":572,"label":573,"href":574},"routes.pricing","Pricing route","\u002Fpricing",{"key":576,"label":577,"href":578},"routes.security","Security route","\u002Fsecurity",{"key":580,"label":581,"href":49},"routes.enterprise","Enterprise route",{"key":583,"label":584,"href":585},"routes.thankYou","Thank you \u002F download route","\u002Fthank-you",{},"data\u002Fshared\u002Furls","P27xKEauu8D-8sfyr0wR4giF0teFSaCuAQ8kgcICQdI",{"id":590,"title":591,"author":592,"authorPhoto":593,"authorPhotoAlt":594,"authorSlug":595,"body":596,"buttonText":1069,"buttonUrl":1070,"category":1071,"date":1072,"description":1073,"draft":1074,"editorsPick":20,"extension":1075,"featured":1074,"image":1076,"imageAlt":594,"meta":1077,"navigation":20,"ogImage":594,"ogImageAlt":594,"path":1078,"seo":1079,"stem":1080,"tags":594,"__hash__":1081},"blog\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk.md","Building daily stand-up generator using Pieces API — Part 1: The SDK overview","Bishoy Hany","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fbuilding-pieces-productivity-with-flutter-ui\u002Fauthor.jpeg",null,"bishoy-hany",{"type":597,"value":598,"toc":1055},"minimark",[599,603,606,611,614,621,624,645,648,652,666,670,673,683,695,701,704,707,711,714,720,723,726,732,735,739,742,745,751,754,758,761,764,770,777,783,786,801,804,810,821,835,838,844,848,851,857,860,866,870,877,884,890,895,898,904,908,915,921,925,932,938,942,952,955,972,975,981,984,988,994,1000,1003,1009,1012,1016,1036,1041,1047],[600,601,602],"p",{},"Imagine the following scenario: Monday, 9 AM. You grab your coffee, join the stand-up call, and suddenly realise: you have absolutely no idea what you did last week. The weekend wiped your mental cache clean. Was it the React component refactor? Or was that two weeks ago? You vaguely remember fighting with TypeScript errors on Thursday, but what was the actual solution? Your Git commits say 'fix: update logic' — thanks, past you, very helpful.",[600,604,605],{},"Here's the thing: your brain isn't built to be a perfect activity log. But your computer? It remembers everything. That's where PiecesOS comes in.",[607,608,610],"h2",{"id":609},"the-goal","The goal",[600,612,613],{},"Create an app that tracks what work is done every day.",[600,615,616],{},[617,618],"img",{"alt":619,"src":620},"","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk\u002Fimg-001.png",[600,622,623],{},"For Part 1, we will work on:",[625,626,627,636,639,642],"ul",{},[628,629,630],"li",{},[631,632,635],"a",{"href":206,"rel":633},[634],"nofollow","Connecting to PiecesOS",[628,637,638],{},"Getting our workstream summaries",[628,640,641],{},"Grouping the summaries by day",[628,643,644],{},"Keeping everything updated in real-time",[600,646,647],{},"We’ll hit a UI later on. Let’s take this one step at a time!",[607,649,651],{"id":650},"prerequisites","Prerequisites",[625,653,654,657],{},[628,655,656],{},"Dart SDK version 3.8.1 or higher (flutter.dev)",[628,658,659,660,665],{},"Gemini API key (will mention that in ",[631,661,664],{"href":662,"rel":663},"https:\u002F\u002Fdocs.google.com\u002Fdocument\u002Fu\u002F0\u002Fd\u002F1bEJZpVDyqgZq70ei7BiKdqosOdJDr5EZguXUVcgLLUI\u002Fedit",[634],"part 2",")",[607,667,669],{"id":668},"setting-up","Setting up",[600,671,672],{},"Let’s begin by creating a new flutter app.",[674,675,680],"pre",{"className":676,"code":678,"language":679},[677],"language-text","flutter create daily_recap_app\n","text",[681,682,678],"code",{"__ignoreMap":619},[600,684,685,686,690,691,694],{},"Before you can start using the Pieces SDK, you’ll need to add it to your Dart or Flutter project. Open your project’s ",[687,688,689],"strong",{},"pubspec.yaml"," file and add the following under ‘",[681,692,693],{},"dependencies:’",":",[674,696,699],{"className":697,"code":698,"language":679},[677],"dependencies:\n  # The Pieces SDK\n  pieces_os_client:\n    git:\n      url: https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-dart.git\n\n  web_socket_channel: ^3.0.1\n",[681,700,698],{"__ignoreMap":619},[600,702,703],{},"The SDK is automatically generated from our API setup, so it’s mostly made up of code that helps your app talk to Pieces’ api.",[600,705,706],{},"We also added the web_socket_channel dependency to connect to the Pieces web-socket.",[607,708,710],{"id":709},"step-1-connecting-to-piecesos","Step 1: Connecting to PiecesOS",[600,712,713],{},"Alright, first challenge – how do we even talk to PiecesOS?",[600,715,716,717],{},"Let's first create a new file ",[681,718,719],{},"lib\u002Fservices\u002Fpieces_os_service.dart",[600,721,722],{},"Nothing fancy – just setting up our API clients.",[600,724,725],{},"Now the fun part – actually connecting:",[674,727,730],{"className":728,"code":729,"language":679},[677],"  \u002F\u002F existing code from above\n  Future\u003CApplication> connectApplication() async {\n    final seededApp = SeededTrackedApplication(\n      name: ApplicationNameEnum.OPEN_SOURCE,  \u002F\u002F Hey Pieces, I'm open source!\n      platform: Platform.operatingSystem == \"macos\"\n          ? PlatformEnum.MACOS\n          : PlatformEnum.WINDOWS,\n      version: \"0.0.1\",\n    );\n\n    final connection = SeededConnectorConnection(\n      application: seededApp,\n    );\n\n    _context = await _connectorApi.connect(\n      seededConnectorConnection: connection,\n    );\n\n    print('Successfully connected to Pieces OS!');\n    return _context!.application;\n  }\n",[681,731,729],{"__ignoreMap":619},[600,733,734],{},"Basically, we're saying, \"Hey Pieces, I'm a new app, let me in!\" and it gives us back a context with our application info.",[607,736,738],{"id":737},"step-2-the-websocket","Step 2: The WebSocket",[600,740,741],{},"This websocket specifically sends us all of the IDs for the users' workstream summaries in Pieces upon first connection. After, it sends only the IDs of either newly created summaries, summaries that have updated data, or summaries that were deleted.",[600,743,744],{},"Here's the setup:",[674,746,749],{"className":747,"code":748,"language":679},[677],"  \u002F\u002F existing code from above\n  void _startWebSocketListener() {\n    final wsUrl = '$websocketUrl\u002Fworkstream_summaries\u002Fstream\u002Fidentifiers';\n    _wsChannel = WebSocketChannel.connect(Uri.parse(wsUrl));\n\n    print('WebSocket connected to $wsUrl');\n\n    _wsSubscription = _wsChannel!.stream.listen(\n      (message) async {\n        try {\n          \u002F\u002F Important: WebSocket sends JSON strings, decode them first!\n          final streamedIdentifiers = StreamedIdentifiers.fromJson(\n            jsonDecode(message),\n          );\n\n          \u002F\u002F Loop through each identifier we received\n          for (final id in streamedIdentifiers!.iterable) {\n            final summaryId = id.workstreamSummary?.id;\n            if (summaryId != null) {\n              await _fetchAndCacheSummary(summaryId);\n            }\n          }\n        } catch (e) {\n          print('Error processing message: $e');\n        }\n      },\n      onDone: () {\n        print('WebSocket closed, reconnecting...');\n        _reconnectWebSocket();\n      },\n      onError: (error) {\n        print('WebSocket error: $error');\n        _reconnectWebSocket();\n      },\n    );\n  }\n",[681,750,748],{"__ignoreMap":619},[600,752,753],{},"Here's the beauty of this approach: The WebSocket just keeps until their app shuts down or they manually close it, sending us identifiers. When we first connect, it dumps ALL existing identifiers on us (could be from a year ago!) and stays open to send us new ones as they're created in real-time. One connection handles everything!",[607,755,757],{"id":756},"step-3-fetching-and-caching-summaries","Step 3: Fetching and caching summaries",[600,759,760],{},"Okay, so we have identifiers. Now what? We need to actually fetch the summary data and organize it.",[600,762,763],{},"Here's what a WorkstreamSummary looks like (the important parts anyway):",[674,765,768],{"className":766,"code":767,"language":679},[677],"\u002F\u002F This is a model defined in the sdks don't add any thing to the code yet.\n\nclass WorkstreamSummary {\n  String id;              \u002F\u002F Unique identifier\n  String? name;           \u002F\u002F Optional name\u002Ftitle\n  GroupedTimestamp created;  \u002F\u002F When it was created\n  GroupedTimestamp? updated; \u002F\u002F When it was last updated\n  \u002F\u002F ... and a bunch of other fields\n}\n",[681,769,767],{"__ignoreMap":619},[600,771,772,773,776],{},"And ",[681,774,775],{},"GroupedTimestamp"," is basically:",[674,778,781],{"className":779,"code":780,"language":679},[677],"\u002F\u002F This is a model defined in the sdks don't add any thing to the code yet.\n\nclass GroupedTimestamp {\n  DateTime value;  \u002F\u002F The actual timestamp\n}\n",[681,782,780],{"__ignoreMap":619},[600,784,785],{},"So when we fetch a summary, we need to:",[787,788,789,792,795,798],"ol",{},[628,790,791],{},"Get the created date",[628,793,794],{},"Strip the time from the date",[628,796,797],{},"Add the summary to that day's list",[628,799,800],{},"Sort everything by time",[600,802,803],{},"Here's the code:",[674,805,808],{"className":806,"code":807,"language":679},[677],"\u002F\u002F existing code from above\n  Future\u003Cvoid> _fetchAndCacheSummary(String identifier) async {\n    final summary = await _workstreamSummaryApi\n        .workstreamSummariesSpecificWorkstreamSummarySnapshot(identifier);\n\n    if (summary == null) {\n      return;\n    }\n    \u002F\u002F Normalize to day (remove time)\n    final createdDate = summary.created.value;\n    final dayKey = DateTime(\n      createdDate.year,\n      createdDate.month,\n      createdDate.day,\n    );\n\n    \u002F\u002F Create day entry if it doesn't exist\n    _summariesByDay.putIfAbsent(dayKey, () => []);\n\n    \u002F\u002F Check for duplicates (avoid adding the same summary twice)\n    final existingIndex = _summariesByDay[dayKey]!\n        .indexWhere((s) => s.id == summary.id);\n\n    if (existingIndex != -1) {\n      \u002F\u002F Update existing\n      _summariesByDay[dayKey]![existingIndex] = summary;\n    } else {\n      \u002F\u002F Add new\n      _summariesByDay[dayKey]!.add(summary);\n    }\n\n    \u002F\u002F Sort by time (most recent first)\n    _summariesByDay[dayKey]!.sort((a, b) {\n      return b.created.value.compareTo(a.created.value);\n    });\n\n    \u002F\u002F Notify anyone listening\n    _summariesStreamController.add(Map.unmodifiable(_summariesByDay));\n\n    print('Cached summary ${summary.id} for day $dayKey');\n  }\n",[681,809,807],{"__ignoreMap":619},[600,811,812,813,816,817,820],{},"The ",[681,814,815],{},"_summariesByDay"," is just a ",[681,818,819],{},"Map\u003CDateTime, List\u003CWorkstreamSummary>>"," where:",[625,822,823,829],{},[628,824,825,828],{},[687,826,827],{},"Key",": A DateTime set to midnight (e.g., Nov 4, 2025 at 00:00:00)",[628,830,831,834],{},[687,832,833],{},"Value",": List of all summaries for that day, sorted by time",[600,836,837],{},"Let's also add some useful methods to retrieve a summary",[674,839,842],{"className":840,"code":841,"language":679},[677]," \u002F\u002F existing code\n  \u002F\u002F\u002F Get summaries for a specific day from cache\n  List\u003CWorkstreamSummary> getSummariesForDay(DateTime date) {\n    final dayKey = DateTime(date.year, date.month, date.day);\n    return _summariesByDay[dayKey] ?? [];\n  }\n\n  \u002F\u002F\u002F Get all days that have summaries (sorted most recent first)\n  List\u003CDateTime> getDaysWithSummaries() {\n    final days = _summariesByDay.keys.toList();\n    days.sort((a, b) => b.compareTo(a)); \u002F\u002F Most recent first\n    return days;\n  }\n",[681,843,841],{"__ignoreMap":619},[607,845,847],{"id":846},"step-4-keeping-everything-in-sync","Step 4: Keeping everything in Sync",[600,849,850],{},"Speaking of streams – let's add a broadcast stream that others can listen to:",[674,852,855],{"className":853,"code":854,"language":679},[677],"\u002F\u002F existing code\nfinal StreamController\u003CMap\u003CDateTime, List\u003CWorkstreamSummary>>>\n    _summariesStreamController = StreamController.broadcast();\n\nStream\u003CMap\u003CDateTime, List\u003CWorkstreamSummary>>> get summariesStream =>\n    _summariesStreamController.stream;\n",[681,856,854],{"__ignoreMap":619},[600,858,859],{},"So in the UI (when we build it), we can just do:",[674,861,864],{"className":862,"code":863,"language":679},[677],"service.summariesStream.listen((summaries) {\n  print('Got new data!');\n  \u002F\u002F Update UI here\n});\n",[681,865,863],{"__ignoreMap":619},[607,867,869],{"id":868},"step-45-waiting-for-initial-websocket-sync","Step 4.5: Waiting for Initial WebSocket Sync",[600,871,872,873,876],{},"Here's a problem: when the app first starts, the WebSocket dumps ",[687,874,875],{},"all"," existing summaries on us. This could be hundreds of identifiers! If we try to show the UI or generate a recap before they're all loaded, we'll have incomplete data.",[600,878,879,880,883],{},"But we only need to wait ",[687,881,882],{},"once"," – on the first message. After that, the WebSocket just sends new updates in real-time and we don't want to block.",[600,885,886,887,694],{},"Here's the solution using a ",[681,888,889],{},"Completer",[891,892,894],"h4",{"id":893},"step-1-add-fields-and-wait-method","Step 1: Add Fields and Wait Method",[600,896,897],{},"Place this code block right after the PiecesOSService() constructor and before the connectApplication() method:",[674,899,902],{"className":900,"code":901,"language":679},[677],"\u002F\u002F Add these fields to the class\nCompleter\u003Cvoid>? _initialSyncCompleter;\nbool get _isInitialSyncComplete =>\n      _initialSyncCompleter?.isCompleted ?? false;\n\u002F\u002F\u002F Wait for the initial WebSocket sync to complete\n\u002F\u002F\u002F Only blocks on the first call, returns immediately after\nFuture\u003Cvoid> waitForInitialSync() async {\n  \u002F\u002F If already synced, return immediately\n  if (_isInitialSyncComplete) {\n    return;\n  }\n\n  \u002F\u002F If sync is in progress, wait for it\n  if (_initialSyncCompleter != null) {\n    return _initialSyncCompleter!.future;\n  }\n\n  \u002F\u002F Start waiting for first sync\n  _initialSyncCompleter = Completer\u003Cvoid>();\n\n  return _initialSyncCompleter!.future;\n}\n",[681,903,901],{"__ignoreMap":619},[891,905,907],{"id":906},"step-2-update-websocket-listener","Step 2: Update WebSocket Listener",[600,909,910,911,914],{},"Replace ",[681,912,913],{},"_wsSubscription"," with this new section:",[674,916,919],{"className":917,"code":918,"language":679},[677],"_wsSubscription = _wsChannel!.stream.listen(\n  (message) async {\n    try {\n      final streamedIdentifiers = StreamedIdentifiers.fromJson(\n        jsonDecode(message),\n      );\n\n      \u002F\u002F Process all identifiers\n      for (final id in streamedIdentifiers!.iterable) {\n        final summaryId = id.workstreamSummary?.id;\n        if (summaryId != null) {\n          await _fetchAndCacheSummary(summaryId);\n        }\n      }\n\n      \u002F\u002F Mark initial sync as complete after first message\n      if (!_isInitialSyncComplete) {\n        _isInitialSyncComplete = true;\n        _initialSyncCompleter?.complete();\n        print('Initial WebSocket sync complete!');\n      }\n    } catch (e) {\n      print('Error processing message: $e');\n    }\n  },\n  \u002F\u002F ... onDone, onError handlers\n);\n",[681,920,918],{"__ignoreMap":619},[891,922,924],{"id":923},"step-3-use-in-app-initialization","Step 3: Use in App Initialization",[600,926,927,928,931],{},"Place this code in your app's entry point (typically ",[681,929,930],{},"lib\u002Fmain.dart",", create the file if you don't have it):",[674,933,936],{"className":934,"code":935,"language":679},[677],"import 'services\u002Fpieces_os_service.dart';\n\nFuture\u003Cvoid> main() async {\n  final service = PiecesOSService();\n\n  await service.connectApplication();\n  service.startWebSocketListener();\n\n  \u002F\u002F Wait for all existing summaries to load\n  await service.waitForInitialSync();\n\n  \u002F\u002F Now we can safely generate recaps or show UI!\n  print('Ready to go! All summaries loaded.');\n}\n",[681,937,935],{"__ignoreMap":619},[607,939,941],{"id":940},"step-5-accessing-the-actual-summary-content","Step 5: Accessing the Actual Summary Content",[600,943,944,947,948,951],{},[681,945,946],{},"WorkstreamSummary"," objects from the SDK don't directly expose the summary text. The actual content is stored in ",[687,949,950],{},"annotations",".",[600,953,954],{},"Each summary has annotations, and we need to:",[787,956,957,960,963,969],{},[628,958,959],{},"Loop through the summary's annotations",[628,961,962],{},"Find the one with type SUMMARY",[628,964,965,966],{},"Fetch that annotation using ",[681,967,968],{},"AnnotationApi",[628,970,971],{},"Extract the text content",[600,973,974],{},"Let's add a method to fetch annotation content:",[674,976,979],{"className":977,"code":978,"language":679},[677],"\u002F\u002F At the bottom of pieces_os_service.dart\n\n\u002F\u002F\u002F Get the summary content from a workstream summary's annotations\nFuture\u003CString?> getSummaryContent(WorkstreamSummary summary) async {\n  try {\n    \u002F\u002F Loop through annotations to find the DESCRIPTION type\n    for (final annotationRef in summary.annotations?.indices.keys.toList() ?? []) {\n      \u002F\u002F Fetch the full annotation using AnnotationApi\n      final annotation = await _annotationApi\n          .annotationSpecificAnnotationSnapshot(annotationRef);\n \n      if (annotation == null) {\n        continue;\n      }\n      \u002F\u002F Check if this is a DESCRIPTION type annotation\n      if (annotation.type == AnnotationTypeEnum.SUMMARY) {\n        \u002F\u002F Return the text content - this is the actual summary!\n        return annotation.text;\n      }\n    }\n\n    return null;\n  } catch (e) {\n    print('Error fetching annotation content for ${summary.id}: $e');\n    return null;\n  }\n}\n",[681,980,978],{"__ignoreMap":619},[600,982,983],{},"The SDK separates metadata (ID, timestamp) from content (stored in annotations).",[607,985,987],{"id":986},"step-6-creating-a-proper-data-structure","Step 6: Creating a Proper Data Structure",[600,989,990,991,694],{},"Let's create a proper class for summaries with their content. Add this to ",[681,992,993],{},"lib\u002Fmodels\u002Fdaily_recap_models.dart",[674,995,998],{"className":996,"code":997,"language":679},[677],"\u002F\u002F\u002F Data class to hold a summary with its content\nclass SummaryWithContent {\n  final String id;\n  final String title;\n  final String content;        \u002F\u002F The actual summary text!\n  final DateTime timestamp;\n\n  SummaryWithContent({\n    required this.id,\n    required this.title,\n    required this.content,\n    required this.timestamp,\n  });\n}\n",[681,999,997],{"__ignoreMap":619},[600,1001,1002],{},"Now, let's add a convenient method to get summaries with their content:",[674,1004,1007],{"className":1005,"code":1006,"language":679},[677],"\u002F\u002F Don't forget to import the model that we just created\nimport 'package:daily_recap_app\u002Fmodels\u002Fdaily_recap_models.dart';\n\n\u002F\u002F Below existing code in pieces_os_straervice.dart\n\n\u002F\u002F\u002F Get summaries with their content for a specific day\nFuture\u003CList\u003CSummaryWithContent>> getSummariesWithContentForDay(\n  DateTime date,\n) async {\n  final summaries = getSummariesForDay(date);\n  final List\u003CSummaryWithContent> summariesWithContent = [];\n\n  for (final summary in summaries) {\n    final content = await getSummaryContent(summary);\n    if (content != null && content.isNotEmpty) {\n      summariesWithContent.add(\n        SummaryWithContent(\n          id: summary.id,\n          title: summary.name,\n          content: content,              \u002F\u002F Actual text from annotation!\n          timestamp: summary.created.value,\n        ),\n      );\n    }\n  }\n\n  return summariesWithContent;\n}\n",[681,1008,1006],{"__ignoreMap":619},[600,1010,1011],{},"Perfect! Now we have rich, typed data ready for AI processing and UI display.",[607,1013,1015],{"id":1014},"part-1-summary","Part 1 Summary",[625,1017,1018,1021,1024,1027,1030,1033],{},[628,1019,1020],{},"Connect to PiecesOS",[628,1022,1023],{},"Open WebSocket for real-time updates",[628,1025,1026],{},"Fetch and cache summaries",[628,1028,1029],{},"Group by day",[628,1031,1032],{},"Stream updates to UI",[628,1034,1035],{},"Extract summary content from annotations",[600,1037,1038,1039],{},"What is created so far ",[681,1040,719],{},[674,1042,1045],{"className":1043,"code":1044,"language":679},[677],"import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'package:pieces_os_client\u002Fapi.dart';\nimport 'package:pieces_os_client\u002Fapi_client.dart';\nimport 'package:web_socket_channel\u002Fweb_socket_channel.dart';\nimport 'package:daily_recap_app\u002Fmodels\u002Fdaily_recap_models.dart';\n\n\u002F\u002F\u002F Service to interact with Pieces OS for LTM (Long Term Memory) tracking\nclass PiecesOSService {\n  static const String baseUrl = 'http:\u002F\u002Flocalhost:39300';\n  static const String websocketUrl = 'ws:\u002F\u002Flocalhost:39300';\n\n  \u002F\u002F Used to register application\n  late final ConnectorApi _connectorApi;\n  \u002F\u002F Used to retrieve the workstream summaries\n  late final WorkstreamSummaryApi _workstreamSummaryApi;\n\n  \u002F\u002F Used to retrieve the summary content\n  late final AnnotationApi _annotationApi;\n\n  final StreamController\u003CMap\u003CDateTime, List\u003CWorkstreamSummary>>>\n      _summariesStreamController = StreamController.broadcast();\n\n  Stream\u003CMap\u003CDateTime, List\u003CWorkstreamSummary>>> get summariesStream =>\n      _summariesStreamController.stream;\n\n  \u002F\u002F Track initial WebSocket sync\n  bool _isInitialSyncComplete = false;\n  Completer\u003Cvoid>? _initialSyncCompleter;\n\n  PiecesOSService() {\n    final client = ApiClient(basePath: baseUrl);\n    _connectorApi = ConnectorApi(client);\n    _workstreamSummaryApi = WorkstreamSummaryApi(client);\n    _annotationApi = AnnotationApi(client);\n  }\n\n  Future\u003CApplication> connectApplication() async {\n    final seededApp = SeededTrackedApplication(\n      name: ApplicationNameEnum.OPEN_SOURCE,  \u002F\u002F Hey Pieces, I'm open source!\n      platform: Platform.operatingSystem == \"macos\"\n          ? PlatformEnum.MACOS\n          : PlatformEnum.WINDOWS,\n      version: \"0.0.1\",\n    );\n\n    final connection = SeededConnectorConnection(\n      application: seededApp,\n    );\n\n    _context = await _connectorApi.connect(\n      seededConnectorConnection: connection,\n    );\n\n    print('Successfully connected to Pieces OS!');\n    return _context!.application;\n  }\n\n  \u002F\u002F\u002F Wait for the initial WebSocket sync to complete\n  \u002F\u002F\u002F Only blocks on the first call, returns immediately after\n  Future\u003Cvoid> waitForInitialSync() async {\n    \u002F\u002F If already synced, return immediately\n    if (_isInitialSyncComplete) {\n      return;\n    }\n\n    \u002F\u002F If sync is in progress, wait for it\n    if (_initialSyncCompleter != null) {\n      return _initialSyncCompleter!.future;\n    }\n\n    \u002F\u002F Start waiting for first sync\n    _initialSyncCompleter = Completer\u003Cvoid>();\n    return _initialSyncCompleter!.future;\n  }\n\n  void _startWebSocketListener() {\n    final wsUrl = '$websocketUrl\u002Fworkstream_summaries\u002Fstream\u002Fidentifiers';\n    _wsChannel = WebSocketChannel.connect(Uri.parse(wsUrl));\n\n    print('WebSocket connected to $wsUrl');\n\n    _wsSubscription = _wsChannel!.stream.listen(\n      (message) async {\n        try {\n          \u002F\u002F Important: WebSocket sends JSON strings, decode them first!\n          final streamedIdentifiers = StreamedIdentifiers.fromJson(\n            jsonDecode(message),\n          );\n\n          \u002F\u002F Loop through each identifier we received\n          for (final id in streamedIdentifiers!.iterable) {\n            final summaryId = id.workstreamSummary?.id;\n            if (summaryId != null) {\n              await _fetchAndCacheSummary(summaryId);\n            }\n          }\n\n          \u002F\u002F Mark initial sync as complete after first message\n          if (!_isInitialSyncComplete) {\n            _isInitialSyncComplete = true;\n            _initialSyncCompleter?.complete();\n            print('Initial WebSocket sync complete!');\n          }\n        } catch (e) {\n          print('Error processing message: $e');\n        }\n      },\n      onDone: () {\n        print('WebSocket closed, reconnecting...');\n        _reconnectWebSocket();\n      },\n      onError: (error) {\n        print('WebSocket error: $error');\n        _reconnectWebSocket();\n      },\n    );\n  }\n\n  Future\u003Cvoid> _fetchAndCacheSummary(String identifier) async {\n    final summary = await _workstreamSummaryApi\n        .workstreamSummariesSpecificWorkstreamSummarySnapshot(identifier);\n\n    if (summary == null) {\n      return;\n    }\n    \u002F\u002F Normalize to day (remove time)\n    final createdDate = summary.created.value;\n    final dayKey = DateTime(\n      createdDate.year,\n      createdDate.month,\n      createdDate.day,\n    );\n\n    \u002F\u002F Create day entry if it doesn't exist\n    if (!_summariesByDay.containsKey(dayKey)) {\n      _summariesByDay[dayKey] = [];\n    }\n\n    \u002F\u002F Check for duplicates (avoid adding the same summary twice)\n    final existingIndex = _summariesByDay[dayKey]!\n        .indexWhere((s) => s.id == summary.id);\n\n    if (existingIndex != -1) {\n      \u002F\u002F Update existing\n      _summariesByDay[dayKey]![existingIndex] = summary;\n    } else {\n      \u002F\u002F Add new\n      _summariesByDay[dayKey]!.add(summary);\n    }\n\n    \u002F\u002F Sort by time (most recent first)\n    _summariesByDay[dayKey]!.sort((a, b) {\n      return b.created.value.compareTo(a.created.value);\n    });\n\n    \u002F\u002F Notify anyone listening\n    _summariesStreamController.add(Map.unmodifiable(_summariesByDay));\n\n    print('Cached summary ${summary.id} for day $dayKey');\n  }\n\n  \u002F\u002F\u002F Get summaries for a specific day from cache\n  List\u003CWorkstreamSummary> getSummariesForDay(DateTime date) {\n    final dayKey = DateTime(date.year, date.month, date.day);\n    return _summariesByDay[dayKey] ?? [];\n  }\n\n  \u002F\u002F\u002F Get all days that have summaries (sorted most recent first)\n  List\u003CDateTime> getDaysWithSummaries() {\n    final days = _summariesByDay.keys.toList();\n    days.sort((a, b) => b.compareTo(a)); \u002F\u002F Most recent first\n    return days;\n  }\n\n  \u002F\u002F\u002F Get ries with their content for a specific day\n  Future\u003CList\u003CSummaryWithContent>> getSummariesWithContentForDay(\n    DateTime date,\n  ) async {\n    final summaries = getSummariesForDay(date);\n    final List\u003CSummaryWithContent> summariesWithContent = [];\n\n    for (final summary in summaries) {\n      final content = await getSummaryContent(summary);\n      if (content != null && content.isNotEmpty) {\n        summariesWithContent.add(\n          SummaryWithContent(\n            id: summary.id,\n            title: summary.name,\n            content: content,\n            timestamp: summary.created.value,\n          ),\n        );\n      }\n    }\n\n    return summariesWithContent;\n  }\n}\n",[681,1046,1044],{"__ignoreMap":619},[600,1048,1049,1054],{},[631,1050,1053],{"href":1051,"rel":1052},"https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fblog-dart-daily-stand-up-generator",[634],"Reference GitHub"," for viewing the full project.",{"title":619,"searchDepth":1056,"depth":1056,"links":1057},2,[1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068],{"id":609,"depth":1056,"text":610},{"id":650,"depth":1056,"text":651},{"id":668,"depth":1056,"text":669},{"id":709,"depth":1056,"text":710},{"id":737,"depth":1056,"text":738},{"id":756,"depth":1056,"text":757},{"id":846,"depth":1056,"text":847},{"id":868,"depth":1056,"text":869},{"id":940,"depth":1056,"text":941},{"id":986,"depth":1056,"text":987},{"id":1014,"depth":1056,"text":1015},"Get started","https:\u002F\u002Fpieces.app\u002F","AI & LLM","2025-12-01T00:00:00.000Z","Learn how to build a daily stand-up generator using the Pieces API. This first part of the series covers the SDK overview, key capabilities, and how developers can streamline workflow automation with Pieces.",false,"md","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk\u002Fhero.png",{},"\u002Fblog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk",{"title":591,"description":1073},"blog\u002Fbuilding-daily-standup-generator-with-pieces-api-sdk","kuDMzksS5PFx5SyaLoLe5I2gd4XL7tIaQYSCw7kWk0Y",{"id":1083,"title":592,"body":1084,"description":1088,"draft":1074,"extension":1075,"meta":1091,"navigation":20,"path":1092,"photo":1093,"photoAlt":594,"seo":1094,"stem":1095,"__hash__":1096},"authors\u002Fauthors\u002Fbishoy-hany.md",{"type":597,"value":1085,"toc":1089},[1086],[600,1087,1088],{},"Bishoy Hany is a Software Engineer at Pieces, where he plays a key role in building developer-first AI experiences that run locally and respect user privacy. With a strong focus on performance, reliability, and intelligent automation, Bishoy works across the full stack to help shape the future of long-term memory and contextual AI.",{"title":619,"searchDepth":1056,"depth":1056,"links":1090},[],{},"\u002Fauthors\u002Fbishoy-hany","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fauthors\u002Fbishoy-hany.jpg",{"title":592,"description":1088},"authors\u002Fbishoy-hany","_m1q8mqNk1I4j1V0_YJSK0zuMS48DBT4Nqw9e3PZjMM"]