Skip to content

feat: import a folder as a project - #210

Merged
brianjfox merged 7 commits into
mainfrom
feat/import-folder
Nov 6, 2026
Merged

brianjfox merged 7 commits into
mainfrom
feat/import-folder

Conversation

@brianjfox

Copy link
Copy Markdown
Contributor

What

Imports a folder, usually a git repository, as a Builder project: from the new-project page, from the command line, and through the operate skill.

Why

Import only took a project file Builder itself had exported, so code that already existed could not become a project. A folder should import as if Builder had made it: the code as the app, any planning documents or design the folder keeps filed with it, and the coordinator writing the rest with the person, for their approval, before anything is built.

Changes

  • packages/schemas/src/app-files.ts: Builder's rules for which files are the app's and how they pack, moved out of the hub and freed of Buffer, so a browser packs code the way the hub packs a coder's workspace. The hub re-exports them.
  • packages/schemas/src/repository-import.ts: readRepository turns a directory's files into the project file the import takes, honouring .gitignore files, finding documents where an export, a seed fixture or a coder workspace keeps them, reporting what is missing, and suggesting files to leave out: names that say secret, and lockfiles the sandbox does not use. ignore becomes a schemas dependency.
  • apps/hub/src/config/import.ts, apps/hub/src/projects/routes.ts, workflows/coordinator/src/prompt.ts: the opening message of an import that filed code without its documents names them, and the coordinator learns the app from its code, asks the person what the code cannot tell it, writes the brief and has the writers produce the rest in order, each for approval.
  • apps/web/src/api/repository.ts and apps/web/src/pages/projects.tsx: Import a folder beside Import project. Chromium's folder picker is walked without entering dependencies or .git; other browsers use a folder input. A card shows the title, the code's size, the documents found and the ones Builder will write, and the suggested exclusions, ticked from the start and keepable.
  • scripts/import-repo.ts (bun run import:repo): the same reading from the command line, listing files with git when the directory is a repository, with --drafts, --approve, --exclude and --allow-missing for an agent that drafts the missing documents first.
  • skills/operate-corbits-builder/SKILL.md: an "Import a directory" procedure that drafts each missing document the way Builder's writers do and gets the person's approval of each before importing.
  • docs/USER_MANUAL.md section 8, docs/DEV.md and README.md describe the folder import and the script.
  • Tests: packages/schemas/src/repository-import.test.ts, apps/web/src/api/repository.test.ts, test/integration/import-repo.test.ts.

Move the rules for which of a workspace's files are the app's, and how
they pack as an app document, from the hub's app snapshot into
packages/schemas as app-files.ts, free of Node's Buffer, so a browser can
pack a person's own code the way the hub packs a coder's workspace. The
hub re-exports them; its walk of a workspace stays where it was.
readRepository turns a directory's files, usually a git repository, into
the project file the import takes, as if Builder had made the project:
the code becomes the app document, honouring the directory's .gitignore
files and the app rules, and the planning documents and the design are
taken from the places a Builder export, a seed fixture or a coder
workspace keeps them. The report names each document Builder expects
that the directory lacks, and suggests files to leave out: those whose
names say they hold a secret, and lockfiles the sandbox does not use.
…acks

When an import files the app's code without some of its documents, as a
person's own repository does, the opening message names them and asks the
coordinator to learn the app from its code, ask the person what the code
cannot tell it, write the brief itself and have the writers produce the
rest in Builder's order, each for the person's approval. The coordinator's
prompt says the same for an imported project.
Add Import a folder beside Import project on the new-project page. The
folder is read in the browser: Chromium's folder picker is walked without
entering dependencies or .git, and other browsers list the folder's files
through a folder input. A card shows the title, the code's size, the
documents found and the ones Builder will write with the person, and the
files suggested for leaving out, ticked from the start, which they can
keep. Importing files the project and opens it.

The user manual describes it.
bun run import:repo <directory> writes the directory as the project file
Import project reads, listing its files with git when it is a repository,
and reports which of Builder's documents it lacks so an agent can draft
them, have the person approve them and run again with --drafts. The
operate-corbits-builder skill gains an Import a directory procedure built
on it; README and DEV.md name the script.

@corbits-triage corbits-triage Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Triage requests changes:

  • Conventions: added code matches a code pattern
    • apps/web/src/pages/projects.tsx:297 promise-chain: Promise chain; use async/await with try/catch.
    • packages/schemas/src/app-files.ts:86 nested-ternary: Nested ternary; use if statements or a lookup.
    • packages/schemas/src/repository-import.ts:489 empty-list-fallback: Invented fallback `?? []`; handle the missing value instead.
    • packages/schemas/src/repository-import.ts:498 empty-list-fallback: Invented fallback `?? []`; handle the missing value instead.
    • packages/schemas/src/repository-import.ts:191-195 nested-ternary: Nested ternary; use if statements or a lookup.
    • packages/schemas/src/repository-import.ts:216-223 promise-chain: Promise chain; use async/await with try/catch.
    • scripts/import-repo.ts:26 zero-fallback: Invented fallback `?? 0`; handle the missing value instead.
    • scripts/import-repo.ts:106 empty-list-fallback: Invented fallback `?? []`; handle the missing value instead.
  • Invented fallbacks: added code matches a code pattern
    • packages/schemas/src/app-files.ts:75 empty-fallback: Invented fallback (an empty string, list, object or zero); handle the missing value instead.
    • packages/schemas/src/repository-import.ts:381 empty-fallback: Invented fallback (an empty string, list, object or zero); handle the missing value instead.
    • packages/schemas/src/repository-import.ts:389 empty-fallback: Invented fallback (an empty string, list, object or zero); handle the missing value instead.
    • packages/schemas/src/repository-import.ts:474 empty-fallback: Invented fallback (an empty string, list, object or zero); handle the missing value instead.
    • packages/schemas/src/repository-import.ts:478 empty-fallback: Invented fallback (an empty string, list, object or zero); handle the missing value instead.

…s from the folder import

The repository reader handles a missing option instead of reading it as
empty, decides a file's suggested reason with if statements and decodes a
file's text through an awaited helper; the app file rules compare keys
with if statements and add binaries in a loop; the page awaits the folder
picker; the script reads a listed file's size from its stat.
@corbits-triage

corbits-triage Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Triage: Pass

No changes needed from you. A maintainer will review this pull request.

Stacked on #209: only this pull request's own changes were checked.

Notes:

  • changes sensitive paths: locks: bun.lock

@corbits-triage
corbits-triage Bot dismissed their stale review November 6, 2026 14:06

Nothing triage asked to change still fails, so this request no longer stands.

return IMPORTED_PROJECT_LEAD + filed + (withApp ? IMPORT_APP_MESSAGE : "");
const unwritten =
withApp && missing.length
? ` The code came from my own repository, so these documents were never written: ${missing.join(", ")}. Read the code with read_artifact, its package.json and README first, and write the brief yourself from what it shows, asking me only what the code cannot tell you; then have the writers produce the rest in order, each instruction carrying what you learned from the code, and have me approve each one before the next.`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · Opening message always tells the coordinator to write the brief, even when the brief was imported

The message lists only the missing kinds, but its instruction still says to write the brief and then have the writers produce the rest. If the import already includes problem_brief and lacks only later documents such as design or build_plan, the coordinator is told to write a brief anyway. That can replace or duplicate the person's imported brief and send them through approvals they don't need.

Import a folder whose files include the app and a problem_brief but no design or build_plan.

Resolve this comment. Triage will not finish while it is open.

if (folderInput.current) folderInput.current.value = "";
}
};
const pickFolder = async () => chooseFolder(await pickDirectory());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · Folder picker errors are not caught and the walk runs without a busy state

pickDirectory() is awaited before chooseFolder and outside its try/catch. It walks every file in the folder and rethrows anything that is not an AbortError. When the button calls void pickFolder(), such an error becomes an unhandled promise rejection and no toast appears. Also, busy is not set during the walk, which can be slow on a large folder, so the buttons stay enabled and a second import can start.

In Chromium, pick a folder where getFile() fails (permission denied, or a file that cannot be read), or pick a large tree and click again while it is still being walked.

Resolve this comment. Triage will not finish while it is open.

Comment on lines +302 to +303
if (!under(directory, path) || excludedPath(within(directory, path)))
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · A malformed approaches.json aborts the whole import

Every other bad input here is skipped: bad JSON makes parseJson return undefined, and a bad project.json or design is ignored. But JSON that parses and does not fit the schema makes ApproachesMeta.assert throw, so readRepository rejects and nothing is imported.

A repository whose docs/approaches.json is valid JSON but not in Builder's ApproachesMeta shape, e.g. {} or a file from some other tool.

Resolve this comment. Triage will not finish while it is open.

Comment thread scripts/import-repo.ts
title: options.title,
approve: options.approve,
exclude,
gitignore: !git,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · Drafts directory is filtered through git's ignore rules, so drafted documents can silently vanish

Drafts are listed with the same code as the app (listDirectory). That code uses git ls-files --exclude-standard whenever the drafts folder sits inside any git work tree. If that repository's .gitignore matches the drafts, git leaves them out and nothing reports it. The documents then show as missing and the import exits 2, even though the person approved drafts for them.

--drafts points at a gitignored folder inside a git work tree, e.g. repo/tmp/drafts with tmp/ in .gitignore.

Resolve this comment. Triage will not finish while it is open.

corbits-triage[bot]
corbits-triage Bot previously approved these changes Nov 6, 2026
@brianjfox
brianjfox changed the base branch from feat/no-app-size-limit to main November 6, 2026 15:44
@brianjfox
brianjfox dismissed corbits-triage[bot]’s stale review November 6, 2026 15:44

The base branch was changed.

@brianjfox
brianjfox added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 562d8f7 Nov 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant