frizz 0.13.9 → 0.13.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/frizz.js +50 -40
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -254,7 +254,7 @@ Frizz has its own small vocabulary. Most of it names a feature, so this doubles
254
254
  | **Snooze** | Hide a card until later — an hour, tomorrow morning, or a date you pick — optionally with a follow-up prompt attached. |
255
255
  | **Goal** | A standing prompt a thread receives on its own — every time it rests, on a clock, or both — until you switch it off or the agent says it's done. |
256
256
  | **Scratchpad** | A thread's durable working memory, readable under its **Doc** tab. Where a worker keeps what a summary would otherwise lose: the approach, the alternatives it rejected, the decisions you made and reversed. |
257
- | **`FRIZZ.md`** | An optional file at your repo root whose contents are injected into every thread, for when you want agents to follow your repo's own norms. Only agents Frizz starts read it. A project with no threads and no `FRIZZ.md` opens on a short questionnaire — how agents land work, how independently they act, anything else they should know — that writes one for you. |
257
+ | **`FRIZZ.md`** | An optional file at your repo root whose contents are injected into every thread, for when you want agents to follow your repo's own norms. Only agents Frizz starts read it. |
258
258
 
259
259
  <br/>
260
260
 
@@ -267,7 +267,7 @@ Frizz has its own small vocabulary. Most of it names a feature, so this doubles
267
267
 
268
268
  <h2 align="center">Contributing</h2>
269
269
 
270
- Issues and pull requests are welcome. Fork the repo, branch off `main`, and open the PR against `main` — CI runs on every pull request.
270
+ Issues and pull requests are welcome. Fork the repo, branch off `main`, and open the PR against `main` — CI runs on every pull request. Keep **Allow edits by maintainers** checked (GitHub offers it only on forks in a personal account, not an organization), so a follow-up commit from the maintainer can land in your PR rather than separately on `main`.
271
271
 
272
272
  Three checks run in CI, and they need no install:
273
273
 
package/dist/frizz.js CHANGED
@@ -862,6 +862,11 @@ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3, relative
862
862
  function legacyFrizzRoot(home = homedir2()) {
863
863
  return join3(home, LEGACY_DIR_NAME);
864
864
  }
865
+ function isLegacyStateRoot(root, exists) {
866
+ if (!exists(root)) return false;
867
+ if (!exists(join3(root, ".id")) && !exists(join3(root, ".gitignore"))) return true;
868
+ return exists(join3(root, "projects")) || exists(join3(root, "registry.json"));
869
+ }
865
870
  function xdg(env, name) {
866
871
  const value = env[name];
867
872
  return value && (value.startsWith("/") || /^[A-Za-z]:[\\/]/u.test(value)) ? value : void 0;
@@ -904,7 +909,7 @@ function frizzPaths(options2 = {}) {
904
909
  const home = options2.home ?? homedir2();
905
910
  const exists = options2.exists ?? existsSync2;
906
911
  const legacyRoot = legacyFrizzRoot(home);
907
- if (exists(legacyRoot)) {
912
+ if (isLegacyStateRoot(legacyRoot, exists)) {
908
913
  return { data: legacyRoot, state: legacyRoot, cache: legacyRoot, legacy: true };
909
914
  }
910
915
  const explicit = {
@@ -13275,31 +13280,6 @@ var init_drainable_worker = __esm({
13275
13280
  }
13276
13281
  });
13277
13282
 
13278
- // packages/shared/src/frizz-md.ts
13279
- var FrizzMdLanding, FrizzMdAutonomy, FRIZZ_MD_NOTES_MAX, FrizzMdAnswers, FrizzMdStatus;
13280
- var init_frizz_md = __esm({
13281
- "packages/shared/src/frizz-md.ts"() {
13282
- "use strict";
13283
- init_zod();
13284
- FrizzMdLanding = external_exports.enum(["pull-request", "main", "main-push", "branch"]);
13285
- FrizzMdAutonomy = external_exports.enum(["decide", "balanced", "check-in"]);
13286
- FRIZZ_MD_NOTES_MAX = 8e3;
13287
- FrizzMdAnswers = external_exports.object({
13288
- landing: FrizzMdLanding,
13289
- autonomy: FrizzMdAutonomy,
13290
- notes: external_exports.string().max(FRIZZ_MD_NOTES_MAX)
13291
- }).strict();
13292
- FrizzMdStatus = external_exports.object({
13293
- /** A FRIZZ.md is already at the project root: the project has its rules, so nothing is asked. */
13294
- exists: external_exports.boolean(),
13295
- /** The operator skipped the questionnaire for this project: it is not asked again. */
13296
- skipped: external_exports.boolean(),
13297
- /** The branch the landing rules name — the remote's default, else the checked-out one, else `main`. */
13298
- defaultBranch: external_exports.string()
13299
- });
13300
- }
13301
- });
13302
-
13303
13283
  // packages/shared/src/receipt-bus.ts
13304
13284
  var init_receipt_bus = __esm({
13305
13285
  "packages/shared/src/receipt-bus.ts"() {
@@ -13547,7 +13527,7 @@ function requireEffortOutsideAcp(profile, ctx) {
13547
13527
  function fallbackPort(base) {
13548
13528
  return base + 1e4;
13549
13529
  }
13550
- var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ClaudeModel, AcpAgent, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, SECRET_TTL_MS, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
13530
+ var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ClaudeModel, AcpAgent, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, SECRET_TTL_MS, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, SteerThreadInput, SteerThreadResult, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
13551
13531
  var init_src = __esm({
13552
13532
  "packages/shared/src/index.ts"() {
13553
13533
  "use strict";
@@ -13560,7 +13540,6 @@ var init_src = __esm({
13560
13540
  init_code_fences();
13561
13541
  init_delta();
13562
13542
  init_drainable_worker();
13563
- init_frizz_md();
13564
13543
  init_interactions();
13565
13544
  init_receipt_bus();
13566
13545
  init_relay_protocol();
@@ -14859,6 +14838,19 @@ var init_src = __esm({
14859
14838
  // ambiguous, and only the operator decides to risk a second copy.
14860
14839
  supersedes: external_exports.string().min(1).max(200).optional()
14861
14840
  });
14841
+ SteerThreadInput = external_exports.object({
14842
+ from: ThreadSlug,
14843
+ to: ThreadSlug,
14844
+ message: external_exports.string().min(1).max(1e5)
14845
+ }).strict();
14846
+ SteerThreadResult = external_exports.object({
14847
+ slug: ThreadSlug,
14848
+ title: external_exports.string(),
14849
+ // What the target was doing when the message arrived: `running` ⇒ it reads it at its next tool
14850
+ // boundary; `resting` ⇒ the message started its next turn; `reopened` ⇒ it was marked done and this
14851
+ // message put it back on the board.
14852
+ delivery: external_exports.enum(["running", "resting", "reopened"])
14853
+ }).strict();
14862
14854
  DismissFailedFollowUpInput = external_exports.object({
14863
14855
  slug: ThreadSlug,
14864
14856
  deliveryId: external_exports.string().min(1).max(200)
@@ -21113,6 +21105,13 @@ function isHomeDirectory(dir, home = homedir8()) {
21113
21105
  return false;
21114
21106
  }
21115
21107
  }
21108
+ function isStateRootHome(dir, home = homedir8()) {
21109
+ return isHomeDirectory(dir, home) && frizzPaths({ home }).legacy;
21110
+ }
21111
+ function refuseStateRootHome(dir, home = homedir8()) {
21112
+ if (isStateRootHome(dir, home))
21113
+ throw new Error("Frizz keeps its own files in ~/.frizz on this machine, so the home folder cannot be a project here \u2014 choose a folder inside it.");
21114
+ }
21116
21115
  function isWorldWritable(dir, platform = process.platform) {
21117
21116
  if (platform === "win32") return false;
21118
21117
  try {
@@ -21149,7 +21148,7 @@ function discoverProjectRoot(cwd = process.cwd(), home = homedir8()) {
21149
21148
  const stop = resolve3(home);
21150
21149
  const filesystemRoot = parse(dir).root;
21151
21150
  for (let at = dir; ; at = dirname8(at)) {
21152
- if (at === stop || at === filesystemRoot) break;
21151
+ if (at === stop || at === filesystemRoot || isHomeDirectory(at, home)) break;
21153
21152
  if (existsSync4(projectIdPath(at))) return at;
21154
21153
  if (hasAny(at, REPO_MARKERS)) return at;
21155
21154
  if (hasAny(at, PROJECT_MARKERS)) return at;
@@ -21161,6 +21160,7 @@ var FRIZZ_DIR, ID_FILE, SELF_IGNORE, REPO_MARKERS, PROJECT_MARKERS;
21161
21160
  var init_project_root = __esm({
21162
21161
  "packages/server/src/project-root.ts"() {
21163
21162
  "use strict";
21163
+ init_frizz_paths();
21164
21164
  init_project_identity();
21165
21165
  FRIZZ_DIR = ".frizz";
21166
21166
  ID_FILE = ".id";
@@ -21199,11 +21199,13 @@ function readRegistry(home = homedir9()) {
21199
21199
  try {
21200
21200
  const parsed = JSON.parse(raw);
21201
21201
  if (parsed?.version !== 1 || !Array.isArray(parsed.projects)) return { ...EMPTY, projects: [] };
21202
+ const forgotten = Array.isArray(parsed.forgotten) ? parsed.forgotten.filter((id) => typeof id === "string") : [];
21202
21203
  return {
21203
21204
  version: 1,
21204
21205
  projects: parsed.projects.filter(
21205
21206
  (p) => p && typeof p.id === "string" && typeof p.path === "string" && typeof p.slug === "string"
21206
- )
21207
+ ),
21208
+ ...forgotten.length > 0 ? { forgotten } : {}
21207
21209
  };
21208
21210
  } catch {
21209
21211
  return { ...EMPTY, projects: [] };
@@ -21268,6 +21270,10 @@ function canonicalPath(dir) {
21268
21270
  function registerProject(input, home = homedir9()) {
21269
21271
  input = { ...input, dir: canonicalPath(input.dir) };
21270
21272
  const registry = readRegistry(home);
21273
+ if (registry.forgotten) {
21274
+ registry.forgotten = registry.forgotten.filter((id) => id !== input.id);
21275
+ if (registry.forgotten.length === 0) delete registry.forgotten;
21276
+ }
21271
21277
  const at = (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
21272
21278
  const byId = registry.projects.find((p) => p.id === input.id);
21273
21279
  if (byId) {
@@ -25640,20 +25646,26 @@ function cleanupSandbox(home) {
25640
25646
  } catch {
25641
25647
  }
25642
25648
  }
25649
+ function emptyLaunchMessage(intent) {
25650
+ return intent.reason === "home" ? "Frizz keeps its own files in ~/.frizz on this machine, so it cannot open your home directory as a project, and there is no other project to show yet. cd into another folder and run frizz there." : `A folder every account can write to cannot be a project: ${intent.directory}. Run frizz in a folder only you can write to.`;
25651
+ }
25643
25652
  function resolveLaunchIntent(cwd = process.cwd(), home = homedir10(), env = process.env) {
25644
25653
  const candidate = realpathSync5(discoverProjectRoot(cwd, home));
25645
25654
  const known = isExistingProjectRoot(candidate) || findByPath(candidate, home) !== void 0;
25646
- const refused = isHomeDirectory(candidate, home) ? "home" : isWorldWritable(candidate) ? "world-writable" : void 0;
25647
- if ((known || hasProjectMarker(candidate)) && !refused)
25655
+ const isHome = isHomeDirectory(candidate, home);
25656
+ const refused = isWorldWritable(candidate) ? "world-writable" : isStateRootHome(candidate, home) ? "home" : void 0;
25657
+ if (!refused && (known || hasProjectMarker(candidate) && !isHome))
25648
25658
  return { kind: "open", workspace: resolveWorkspace(cwd, home, env) };
25649
25659
  const host = mostRecentProject(home, env);
25650
- const reason = refused ?? "unadopted";
25651
- if (!host) return { kind: "empty", directory: candidate, reason };
25652
- return reason === "unadopted" ? { kind: "offer", workspace: host, directory: candidate } : { kind: "grid", workspace: host };
25660
+ if (!host) {
25661
+ if (refused) return { kind: "empty", directory: candidate, reason: refused };
25662
+ return { kind: "open", workspace: resolveWorkspace(cwd, home, env) };
25663
+ }
25664
+ return refused || isHome ? { kind: "grid", workspace: host } : { kind: "offer", workspace: host, directory: candidate };
25653
25665
  }
25654
25666
  function mostRecentProject(home, env) {
25655
25667
  for (const entry of listProjects(home)) {
25656
- if (entry.stale || isHomeDirectory(entry.path, home) || isWorldWritable(entry.path)) continue;
25668
+ if (entry.stale || isStateRootHome(entry.path, home) || isWorldWritable(entry.path)) continue;
25657
25669
  try {
25658
25670
  return resolveWorkspace(entry.path, home, env);
25659
25671
  } catch {
@@ -25677,6 +25689,7 @@ function resolveWorkspace(cwd = process.cwd(), home = homedir10(), env = process
25677
25689
  }
25678
25690
  const root0 = realpathSync5(gitRoot ?? discoverProjectRoot(cwd, home));
25679
25691
  refuseWorldWritableProject(root0);
25692
+ refuseStateRootHome(root0, home);
25680
25693
  const identity = gitRoot ? resolveGitProjectIdentity(root0, home) : void 0;
25681
25694
  const root = identity?.root ?? root0;
25682
25695
  const id = ensureProjectIdFile(root, home, identity?.id);
@@ -27720,10 +27733,7 @@ var workspace = (() => {
27720
27733
  return workspaceFromLaunchTarget(pinned);
27721
27734
  }
27722
27735
  const intent = resolveLaunchIntent();
27723
- if (intent.kind === "empty")
27724
- throw new Error(
27725
- intent.reason === "home" ? "frizz cannot open your home directory as a project, and there is no other project to show yet. cd into a repository and run frizz there." : intent.reason === "world-writable" ? `A folder every account can write to cannot be a project: ${intent.directory}. Run frizz in a folder only you can write to.` : `${intent.directory} is not a Frizz project yet, and there is no other project to show. Run frizz inside a repository, or add this one from the projects page once a board is open.`
27726
- );
27736
+ if (intent.kind === "empty") throw new Error(emptyLaunchMessage(intent));
27727
27737
  launchIntent = intent;
27728
27738
  return intent.workspace;
27729
27739
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frizz",
3
- "version": "0.13.9",
3
+ "version": "0.13.10",
4
4
  "private": false,
5
5
  "description": "A local web UI for running many coding agents at once",
6
6
  "keywords": [
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "frizzServer": {
68
68
  "package": "frizz-server",
69
- "version": "0.15.18",
69
+ "version": "0.15.20",
70
70
  "protocol": 1,
71
71
  "dataEpoch": 1
72
72
  }