totopo 3.4.0-rc-1 → 3.4.0
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.
- package/README.md +3 -3
- package/dist/commands/dev.js +1 -1
- package/dist/commands/workspace.js +8 -8
- package/dist/lib/agent-context.js +1 -1
- package/dist/lib/migrate-to-latest.js +6 -7
- package/dist/lib/workspace-identity.js +5 -5
- package/package.json +1 -1
- package/templates/startup-git-mode.mjs +1 -1
package/README.md
CHANGED
|
@@ -106,9 +106,9 @@ Each workspace has a git mode (set via **Manage Workspace > Git mode**) that con
|
|
|
106
106
|
|
|
107
107
|
| Mode | Local mutations | Remote (push/pull/fetch/clone) |
|
|
108
108
|
|---|---|---|
|
|
109
|
-
| **
|
|
110
|
-
| **
|
|
111
|
-
| **unrestricted** | Allowed | Allowed
|
|
109
|
+
| **local** *(default)* | Allowed | Blocked at the gitconfig protocol layer |
|
|
110
|
+
| **strict** | Blocked — a read-only `git` wrapper allows inspection (`status`, `log`, `diff`, `blame`, `show`, etc.) and rejects mutations (`commit`, `add`, `reset`, `checkout`, etc.) | Blocked at the gitconfig protocol layer |
|
|
111
|
+
| **unrestricted** | Allowed | Allowed |
|
|
112
112
|
|
|
113
113
|
The active mode is recorded per workspace in `.lock`, exposed inside the container as `TOTOPO_GIT_MODE`, and reflected in the agent context so each session knows what is permitted. Switching modes recreates the container on the next session.
|
|
114
114
|
|
package/dist/commands/dev.js
CHANGED
|
@@ -278,7 +278,7 @@ export async function run(packageDir, ctx, options) {
|
|
|
278
278
|
}
|
|
279
279
|
const hasGit = existsSync(join(workspaceDir, ".git"));
|
|
280
280
|
// --- Git mode (per-workspace, host-side .lock) ---------------------------------------------------------------------------------------
|
|
281
|
-
const gitMode = readGitMode(ctx.workspaceId) ?? GIT_MODE.
|
|
281
|
+
const gitMode = readGitMode(ctx.workspaceId) ?? GIT_MODE.local;
|
|
282
282
|
// --- Start container -----------------------------------------------------------------------------------------------------------------
|
|
283
283
|
const containerOpts = {
|
|
284
284
|
containerName,
|
|
@@ -121,19 +121,19 @@ async function removeShadowPatterns(ctx) {
|
|
|
121
121
|
}
|
|
122
122
|
// --- Git mode menu -----------------------------------------------------------------------------------------------------------------------
|
|
123
123
|
async function gitModeMenu(ctx) {
|
|
124
|
-
const current = readGitMode(ctx.workspaceId) ?? GIT_MODE.
|
|
125
|
-
note("
|
|
126
|
-
"
|
|
124
|
+
const current = readGitMode(ctx.workspaceId) ?? GIT_MODE.local;
|
|
125
|
+
note("Local — local mutations allowed; remote blocked\n" +
|
|
126
|
+
"Strict — read-only; mutations and remote blocked\n" +
|
|
127
127
|
"Unrestricted — no totopo-enforced restrictions", "Git mode");
|
|
128
128
|
const choice = await select({
|
|
129
129
|
message: "Git mode:",
|
|
130
130
|
options: [
|
|
131
131
|
{
|
|
132
|
-
value: GIT_MODE.
|
|
133
|
-
label: "
|
|
134
|
-
hint: current === GIT_MODE.
|
|
132
|
+
value: GIT_MODE.local,
|
|
133
|
+
label: "Local",
|
|
134
|
+
hint: current === GIT_MODE.local ? "current · default" : "default",
|
|
135
135
|
},
|
|
136
|
-
{ value: GIT_MODE.
|
|
136
|
+
{ value: GIT_MODE.strict, label: "Strict", ...(current === GIT_MODE.strict ? { hint: "current" } : {}) },
|
|
137
137
|
{
|
|
138
138
|
value: GIT_MODE.unrestricted,
|
|
139
139
|
label: "Unrestricted",
|
|
@@ -203,7 +203,7 @@ async function resetTotopoYaml(ctx) {
|
|
|
203
203
|
// --- Manage Workspace submenu ------------------------------------------------------------------------------------------------------------
|
|
204
204
|
export async function run(ctx) {
|
|
205
205
|
while (true) {
|
|
206
|
-
const currentGitMode = readGitMode(ctx.workspaceId) ?? GIT_MODE.
|
|
206
|
+
const currentGitMode = readGitMode(ctx.workspaceId) ?? GIT_MODE.local;
|
|
207
207
|
const options = [
|
|
208
208
|
{ value: "git-mode", label: "Git mode", hint: `current: ${currentGitMode}` },
|
|
209
209
|
{ value: "shadow-paths", label: "Shadow paths", hint: "manage shadow patterns" },
|
|
@@ -86,7 +86,7 @@ function renderTemplate(template, vars) {
|
|
|
86
86
|
/**
|
|
87
87
|
* Assembles the agent context markdown injected into each supported agent's config dir at session start.
|
|
88
88
|
*/
|
|
89
|
-
export function buildAgentContextDocs(hasGit, shadowPatterns, gitMode = GIT_MODE.
|
|
89
|
+
export function buildAgentContextDocs(hasGit, shadowPatterns, gitMode = GIT_MODE.local) {
|
|
90
90
|
const gitSection = loadTemplate(hasGit ? `git-mode-${gitMode}` : "git-unavailable");
|
|
91
91
|
const shadowSection = shadowPatterns && shadowPatterns.length > 0
|
|
92
92
|
? renderTemplate(loadTemplate("shadow-paths"), {
|
|
@@ -391,12 +391,11 @@ function migrateRemoveLastCliUpdate() {
|
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
/**
|
|
394
|
-
* Pre-v3.4.0: Add the git_mode field to .lock files.
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* for testing purposes; the registered Migration entry ignores it.
|
|
394
|
+
* Pre-v3.4.0: Add the git_mode=local field to .lock files. Local is the default, so this
|
|
395
|
+
* is a cosmetic write that makes the field visible on disk; runtime behavior is unchanged
|
|
396
|
+
* for existing workspaces. Idempotent - skips files that already have the field. Prints a
|
|
397
|
+
* one-time clack note() when any workspace was newly migrated so users discover the new
|
|
398
|
+
* feature. Returns the count for testing purposes; the registered Migration entry ignores it.
|
|
400
399
|
*/
|
|
401
400
|
export function migrateAddGitMode() {
|
|
402
401
|
const baseDir = getWorkspacesBaseDir();
|
|
@@ -418,7 +417,7 @@ export function migrateAddGitMode() {
|
|
|
418
417
|
}
|
|
419
418
|
}
|
|
420
419
|
if (migrated > 0) {
|
|
421
|
-
note(`totopo v3.4.0 introduces git modes for workspaces.\
|
|
420
|
+
note(`totopo v3.4.0 introduces git modes for workspaces.\nDefault is 'local' (previous behavior — local commits allowed, remote blocked).\nTwo opt-in modes are available: 'strict' (read-only, all mutations blocked) and 'unrestricted' (no totopo-enforced restrictions).\nSwitch via the totopo menu > Manage Workspace > Git mode.`, "Git modes");
|
|
422
421
|
}
|
|
423
422
|
return migrated;
|
|
424
423
|
}
|
|
@@ -54,7 +54,7 @@ function parseLockFile(workspaceId) {
|
|
|
54
54
|
return {
|
|
55
55
|
workspaceRoot: partial.workspaceRoot,
|
|
56
56
|
activeProfile: partial.activeProfile ?? PROFILE.default,
|
|
57
|
-
gitMode: partial.gitMode ?? GIT_MODE.
|
|
57
|
+
gitMode: partial.gitMode ?? GIT_MODE.local,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
catch {
|
|
@@ -78,7 +78,7 @@ export function writeLockFile(workspaceId, workspaceRoot) {
|
|
|
78
78
|
writeLockFileInternal(workspaceId, {
|
|
79
79
|
workspaceRoot,
|
|
80
80
|
activeProfile: existing?.activeProfile ?? PROFILE.default,
|
|
81
|
-
gitMode: existing?.gitMode ?? GIT_MODE.
|
|
81
|
+
gitMode: existing?.gitMode ?? GIT_MODE.local,
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
/** Read the active profile name. Returns null if lock file is missing. */
|
|
@@ -92,13 +92,13 @@ export function writeActiveProfile(workspaceId, profile) {
|
|
|
92
92
|
return;
|
|
93
93
|
writeLockFileInternal(workspaceId, { ...existing, activeProfile: profile });
|
|
94
94
|
}
|
|
95
|
-
/** Read the active git mode. Returns null if lock file is missing. Coerces unknown values to
|
|
95
|
+
/** Read the active git mode. Returns null if lock file is missing. Coerces unknown values to local. */
|
|
96
96
|
export function readGitMode(workspaceId) {
|
|
97
97
|
const parsed = parseLockFile(workspaceId);
|
|
98
98
|
if (!parsed)
|
|
99
99
|
return null;
|
|
100
100
|
const value = parsed.gitMode;
|
|
101
|
-
return GIT_MODES.includes(value) ? value : GIT_MODE.
|
|
101
|
+
return GIT_MODES.includes(value) ? value : GIT_MODE.local;
|
|
102
102
|
}
|
|
103
103
|
/** Write the active git mode. Preserves workspace root path and active profile. */
|
|
104
104
|
export function writeGitMode(workspaceId, gitMode) {
|
|
@@ -109,7 +109,7 @@ export function writeGitMode(workspaceId, gitMode) {
|
|
|
109
109
|
}
|
|
110
110
|
// --- Workspace directory initialization --------------------------------------------------------------------------------------------------
|
|
111
111
|
/** Initialize ~/.totopo/workspaces/<workspace_id>/ with lock file and subdirs. */
|
|
112
|
-
export function initWorkspaceDir(workspaceId, workspaceRoot, activeProfile = PROFILE.default, gitMode = GIT_MODE.
|
|
112
|
+
export function initWorkspaceDir(workspaceId, workspaceRoot, activeProfile = PROFILE.default, gitMode = GIT_MODE.local) {
|
|
113
113
|
const dir = getWorkspaceDir(workspaceId);
|
|
114
114
|
mkdirSync(join(dir, AGENTS_DIR), { recursive: true });
|
|
115
115
|
mkdirSync(join(dir, SHADOWS_DIR), { recursive: true });
|
package/package.json
CHANGED
|
@@ -129,7 +129,7 @@ function verifyRemoteBlocked(gitMode, ok, fail, skip) {
|
|
|
129
129
|
* through the main startup script's section formatting and error counter.
|
|
130
130
|
*/
|
|
131
131
|
export function checkGitMode({ ok, fail, skip, run, isRoot }) {
|
|
132
|
-
const gitMode = VALID_GIT_MODES.includes(process.env.TOTOPO_GIT_MODE) ? process.env.TOTOPO_GIT_MODE : GIT_MODE.
|
|
132
|
+
const gitMode = VALID_GIT_MODES.includes(process.env.TOTOPO_GIT_MODE) ? process.env.TOTOPO_GIT_MODE : GIT_MODE.local;
|
|
133
133
|
const protocolValue = gitMode === GIT_MODE.unrestricted ? "always" : "never";
|
|
134
134
|
|
|
135
135
|
if (isRoot) {
|