@rubytech/create-maxy-code 0.1.223 → 0.1.225

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 (41) hide show
  1. package/dist/__tests__/installer-settings-permissions.test.js +112 -0
  2. package/dist/index.js +13 -0
  3. package/dist/permissions-seed.js +76 -0
  4. package/package.json +1 -1
  5. package/payload/platform/plugins/admin/mcp/dist/index.js +1 -1
  6. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  7. package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +9 -7
  8. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +18 -4
  9. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
  10. package/payload/platform/plugins/docs/references/admin-ui.md +11 -1
  11. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  12. package/payload/platform/plugins/docs/references/platform.md +4 -0
  13. package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
  14. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +1 -1
  15. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts.map +1 -1
  17. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +7 -5
  18. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
  19. package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +6 -6
  20. package/payload/platform/plugins/scheduling/PLUGIN.md +2 -2
  21. package/payload/platform/plugins/scheduling/mcp/dist/index.js +2 -2
  22. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.d.ts.map +1 -1
  24. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js +3 -2
  25. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-export-ics.js.map +1 -1
  26. package/payload/platform/scripts/check-plugin-references.mjs +256 -0
  27. package/payload/platform/scripts/logs-read-jsonl.test.sh +307 -0
  28. package/payload/platform/scripts/logs-read.sh +435 -32
  29. package/payload/platform/scripts/setup-account.sh +20 -8
  30. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +0 -1
  31. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  32. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +11 -10
  33. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  34. package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
  35. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  36. package/payload/premium-plugins/teaching/PLUGIN.md +7 -7
  37. package/payload/premium-plugins/venture-studio/skills/brand-pack/SKILL.md +1 -1
  38. package/payload/server/public/assets/data-BzRzOKWv.js +1 -0
  39. package/payload/server/public/data.html +1 -1
  40. package/payload/server/server.js +13 -6
  41. package/payload/server/public/assets/data-CHdaTjgn.js +0 -1
@@ -0,0 +1,112 @@
1
+ // Task 583 — Installer must seed permissions.defaultMode=bypassPermissions
2
+ // and permissions.allow=["*"] into the brand-scoped settings.json so
3
+ // claude.ai/code sessions never fall through to the Anthropic remote
4
+ // auto-classifier. Existing top-level keys must be preserved; an
5
+ // already-correct file must be a true no-op (mtime unchanged).
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { mkdtempSync, readFileSync, writeFileSync, statSync, rmSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { seedBypassPermissionsSettings, assertBypassPermissionsSeed, BYPASS_PERMISSIONS_BLOCK } from "../permissions-seed.js";
12
+ function freshDir() {
13
+ return mkdtempSync(join(tmpdir(), "t583-"));
14
+ }
15
+ test("empty target: creates settings.json with the permissions block", () => {
16
+ const dir = freshDir();
17
+ try {
18
+ const result = seedBypassPermissionsSettings(dir);
19
+ assert.equal(result.action, "seeded");
20
+ assert.equal(result.path, join(dir, "settings.json"));
21
+ const parsed = JSON.parse(readFileSync(result.path, "utf-8"));
22
+ assert.deepEqual(parsed, { permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } });
23
+ assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
24
+ }
25
+ finally {
26
+ rmSync(dir, { recursive: true, force: true });
27
+ }
28
+ });
29
+ test("existing keys preserved verbatim; permissions added", () => {
30
+ const dir = freshDir();
31
+ try {
32
+ const settingsPath = join(dir, "settings.json");
33
+ const existing = {
34
+ enabledPlugins: { "memory@claude-plugins-official": true },
35
+ extraKnownMarketplaces: { "claude-plugins-official": { source: { source: "github", repo: "anthropics/claude-plugins-official" } } },
36
+ inputNeededNotifEnabled: false,
37
+ agentPushNotifEnabled: false,
38
+ model: "opus",
39
+ };
40
+ writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
41
+ const result = seedBypassPermissionsSettings(dir);
42
+ assert.equal(result.action, "seeded");
43
+ const parsed = JSON.parse(readFileSync(settingsPath, "utf-8"));
44
+ assert.deepEqual(parsed.enabledPlugins, existing.enabledPlugins);
45
+ assert.deepEqual(parsed.extraKnownMarketplaces, existing.extraKnownMarketplaces);
46
+ assert.equal(parsed.inputNeededNotifEnabled, false);
47
+ assert.equal(parsed.agentPushNotifEnabled, false);
48
+ assert.equal(parsed.model, "opus");
49
+ assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: ["*"], deny: [] });
50
+ assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
51
+ }
52
+ finally {
53
+ rmSync(dir, { recursive: true, force: true });
54
+ }
55
+ });
56
+ test("idempotent: already-correct block triggers no write", () => {
57
+ const dir = freshDir();
58
+ try {
59
+ const settingsPath = join(dir, "settings.json");
60
+ writeFileSync(settingsPath, JSON.stringify({ model: "opus", permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } }, null, 2));
61
+ const before = statSync(settingsPath).mtimeMs;
62
+ // Small busy-wait so a same-millisecond write would be detectable on
63
+ // filesystems with millisecond mtime granularity.
64
+ const target = Date.now() + 15;
65
+ while (Date.now() < target) { /* spin */ }
66
+ const result = seedBypassPermissionsSettings(dir);
67
+ assert.equal(result.action, "noop");
68
+ const after = statSync(settingsPath).mtimeMs;
69
+ assert.equal(after, before, "mtime must not change on noop");
70
+ }
71
+ finally {
72
+ rmSync(dir, { recursive: true, force: true });
73
+ }
74
+ });
75
+ test("malformed JSON: overwrites with the seeded block", () => {
76
+ const dir = freshDir();
77
+ try {
78
+ const settingsPath = join(dir, "settings.json");
79
+ writeFileSync(settingsPath, "{ not json");
80
+ const result = seedBypassPermissionsSettings(dir);
81
+ assert.equal(result.action, "seeded");
82
+ assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
83
+ }
84
+ finally {
85
+ rmSync(dir, { recursive: true, force: true });
86
+ }
87
+ });
88
+ test("assert: missing file reports missing", () => {
89
+ const dir = freshDir();
90
+ try {
91
+ assert.equal(assertBypassPermissionsSeed(dir).status, "missing");
92
+ }
93
+ finally {
94
+ rmSync(dir, { recursive: true, force: true });
95
+ }
96
+ });
97
+ test("assert: partial block (no wildcard) reports missing", () => {
98
+ const dir = freshDir();
99
+ try {
100
+ writeFileSync(join(dir, "settings.json"), JSON.stringify({ permissions: { defaultMode: "default", allow: [], deny: [] } }));
101
+ assert.equal(assertBypassPermissionsSeed(dir).status, "missing");
102
+ }
103
+ finally {
104
+ rmSync(dir, { recursive: true, force: true });
105
+ }
106
+ });
107
+ test("BYPASS_PERMISSIONS_BLOCK frozen literal matches expected shape", () => {
108
+ assert.equal(BYPASS_PERMISSIONS_BLOCK.defaultMode, "bypassPermissions");
109
+ assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.allow], ["*"]);
110
+ assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.deny], []);
111
+ assert.ok(Object.isFrozen(BYPASS_PERMISSIONS_BLOCK));
112
+ });
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { execFileSync, spawn, spawnSync } from "node:child_process";
3
3
  import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, readdirSync, appendFileSync, openSync, closeSync, chmodSync, statSync, realpathSync } from "node:fs";
4
4
  import { registerSpecialistAgentsAt, SpecialistSymlinkCollision } from "./specialist-registration.js";
5
+ import { seedBypassPermissionsSettings, assertBypassPermissionsSeed } from "./permissions-seed.js";
5
6
  import { resolve, join, dirname } from "node:path";
6
7
  import { randomBytes } from "node:crypto";
7
8
  import { resolveInstallPortFromFs, buildMaxyUnitFile, buildClaudeSessionManagerUnitFile, buildClaudePtysSliceUnitFile } from "./port-resolution.js";
@@ -953,6 +954,12 @@ function installClaudeCode() {
953
954
  // pre-create the per-brand CLAUDE_CONFIG_DIR so the first
954
955
  // `claude plugin marketplace add` has a directory to write into.
955
956
  mkdirSync(CLAUDE_CONFIG_DIR, { recursive: true });
957
+ // Task 583 — seed the brand-scoped permissions block so claude.ai/code
958
+ // sessions short-circuit Stage 1 of the local matcher and never fall
959
+ // through to the Anthropic remote auto-classifier (which routes Agent
960
+ // dispatch to `ask` and surfaces a prompt no unattended operator clicks).
961
+ const permissionsSeed = seedBypassPermissionsSettings(CLAUDE_CONFIG_DIR);
962
+ logFile(`[install-permissions] action=${permissionsSeed.action} path=${permissionsSeed.path}`);
956
963
  const marketplaceList = spawnSync("claude", ["plugin", "marketplace", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
957
964
  if (marketplaceList.stderr)
958
965
  process.stderr.write(marketplaceList.stderr);
@@ -2053,6 +2060,12 @@ function registerLocalAndExternalPlugins() {
2053
2060
  throw new Error(`Plugin registration incomplete: expected marketplaces ${JSON.stringify([...expectedMarketplaces])} but \`claude plugin marketplace list\` returned ${JSON.stringify(actualMarketplaces)}. Missing: ${JSON.stringify(missing)}. Check setup.log for [plugin-marketplace] ERROR lines.`);
2054
2061
  }
2055
2062
  logFile(`[plugin-marketplace] assertion-source=claude-plugin-marketplace-list count=${actualMarketplaces.length} marketplaces=${JSON.stringify(actualMarketplaces)} CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR}`);
2063
+ // Task 583 — post-install assertion that the brand-scoped permissions
2064
+ // block survived install. Warn-only: a missing block does not abort the
2065
+ // install (consistent with the marketplace ERROR path that logs and
2066
+ // continues per-entry), but surfaces in setup.log for diagnosis.
2067
+ const permissionsAssert = assertBypassPermissionsSeed(CLAUDE_CONFIG_DIR);
2068
+ logFile(`[install-permissions] brand-settings=${permissionsAssert.status} path=${permissionsAssert.path}`);
2056
2069
  }
2057
2070
  function buildPlatform() {
2058
2071
  log("9", TOTAL, "Installing dependencies and building...");
@@ -0,0 +1,76 @@
1
+ // Task 583 — Seed Claude Code's two-stage permission classifier with a
2
+ // wildcard allow so claude.ai/code sessions never fall through to the
3
+ // Anthropic remote auto-classifier. The classifier routes `Agent`/`Task`
4
+ // to `ask`, which surfaces as a permission prompt that an unattended
5
+ // operator never clicks — observed as 4-minute dead-air hangs on
6
+ // realagent-code.
7
+ //
8
+ // This module is the single writer for the brand-scoped settings.json
9
+ // permissions block. Account-scoped settings.json is written by
10
+ // platform/scripts/setup-account.sh in lockstep with the same shape.
11
+ import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from "node:fs";
12
+ import { join, dirname } from "node:path";
13
+ export const BYPASS_PERMISSIONS_BLOCK = Object.freeze({
14
+ defaultMode: "bypassPermissions",
15
+ allow: ["*"],
16
+ deny: [],
17
+ });
18
+ function isAlreadyCorrect(permissions) {
19
+ if (!permissions || typeof permissions !== "object")
20
+ return false;
21
+ const p = permissions;
22
+ if (p.defaultMode !== "bypassPermissions")
23
+ return false;
24
+ if (!Array.isArray(p.allow) || !p.allow.includes("*"))
25
+ return false;
26
+ if (!Array.isArray(p.deny) || p.deny.length !== 0)
27
+ return false;
28
+ return true;
29
+ }
30
+ export function seedBypassPermissionsSettings(configDir) {
31
+ const settingsPath = join(configDir, "settings.json");
32
+ let existing = {};
33
+ if (existsSync(settingsPath)) {
34
+ const text = readFileSync(settingsPath, "utf-8");
35
+ if (text.trim().length > 0) {
36
+ try {
37
+ const parsed = JSON.parse(text);
38
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
39
+ existing = parsed;
40
+ }
41
+ }
42
+ catch {
43
+ // Malformed JSON: treat as empty and overwrite with a well-formed
44
+ // file containing only the permissions block. Any keys the prior
45
+ // file carried are unrecoverable from invalid JSON anyway.
46
+ }
47
+ }
48
+ }
49
+ if (isAlreadyCorrect(existing.permissions)) {
50
+ return { action: "noop", path: settingsPath };
51
+ }
52
+ mkdirSync(dirname(settingsPath), { recursive: true });
53
+ const merged = { ...existing, permissions: { ...BYPASS_PERMISSIONS_BLOCK, allow: [...BYPASS_PERMISSIONS_BLOCK.allow], deny: [] } };
54
+ const tmpPath = `${settingsPath}.task583.tmp`;
55
+ writeFileSync(tmpPath, JSON.stringify(merged, null, 2) + "\n");
56
+ renameSync(tmpPath, settingsPath);
57
+ return { action: "seeded", path: settingsPath };
58
+ }
59
+ export function assertBypassPermissionsSeed(configDir) {
60
+ const settingsPath = join(configDir, "settings.json");
61
+ if (!existsSync(settingsPath))
62
+ return { status: "missing", path: settingsPath };
63
+ let parsed;
64
+ try {
65
+ parsed = JSON.parse(readFileSync(settingsPath, "utf-8"));
66
+ }
67
+ catch {
68
+ return { status: "missing", path: settingsPath };
69
+ }
70
+ if (!parsed || typeof parsed !== "object")
71
+ return { status: "missing", path: settingsPath };
72
+ const permissions = parsed.permissions;
73
+ return isAlreadyCorrect(permissions)
74
+ ? { status: "ok", path: settingsPath }
75
+ : { status: "missing", path: settingsPath };
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.223",
3
+ "version": "0.1.225",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -1969,7 +1969,7 @@ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill na
1969
1969
  // sentinel that read like a successful render to the agent while the
1970
1970
  // chat surface saw nothing. Admin flows now use plain markdown for
1971
1971
  // reviewable artefacts and the `file-presentation` skill for downloadable
1972
- // artefacts (delivered via `SendUserFile`, the native Claude Code surface).
1972
+ // artefacts (written under output/ and delivered by stating their location).
1973
1973
  // The :Component / :KnowledgeDocument
1974
1974
  // persistence pipeline is preserved for historical conversations that
1975
1975
  // already carry the rows; no new conversations write to it.