refactor: strip internal pi branding (Phase 2A)
- CURSOR_MARKER: \x1b_pi:c\x07 → \x1b_sf:c\x07 - process.title: "pi" → "sf" - PiManifest → SFManifest (with pi field backwards compat) - readPiManifest → readSFManifest (loader.ts and package-manager.ts) - readPiManifestFile → readSFManifestFile (package-manager.ts) - .pi/skills → .sf/skills (keeps .pi/skills for backwards compat) - User-facing path strings updated to .sf/ where appropriate - ARCHITECTURE.md: "Pi coding-agent extension" → "coding-agent extension" - Temp editor file: pi-editor-*.pi.md → sf-editor-*.sf.md - Test fixtures: appName "pi" → "sf", pi manifest field → sf Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
02a4339a51
commit
cab8b5decc
9 changed files with 54 additions and 53 deletions
|
|
@ -11,7 +11,7 @@ Singularity Forge (SF) is the product. It runs long-horizon coding work through
|
||||||
| `src/loader.ts` | Entry point — initializes resources, registers extension |
|
| `src/loader.ts` | Entry point — initializes resources, registers extension |
|
||||||
| `src/headless.ts` | Non-interactive (headless) mode driver — exit codes 0/1/10/11/12 |
|
| `src/headless.ts` | Non-interactive (headless) mode driver — exit codes 0/1/10/11/12 |
|
||||||
| `src/headless-events.ts` | Transcript event parsing and notification routing |
|
| `src/headless-events.ts` | Transcript event parsing and notification routing |
|
||||||
| `src/extension-registry.ts` | Registers SF as a Pi coding-agent extension |
|
| `src/extension-registry.ts` | Registers SF as a coding-agent extension |
|
||||||
| `src/resources/extensions/sf/` | All SF extension source (TypeScript) |
|
| `src/resources/extensions/sf/` | All SF extension source (TypeScript) |
|
||||||
| `src/resources/extensions/sf/auto/` | Autonomous workflow orchestrator (UOK lifecycle, dispatch, planning) |
|
| `src/resources/extensions/sf/auto/` | Autonomous workflow orchestrator (UOK lifecycle, dispatch, planning) |
|
||||||
| `src/resources/extensions/sf/bootstrap/` | Context injection, system prompt assembly |
|
| `src/resources/extensions/sf/bootstrap/` | Context injection, system prompt assembly |
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
*
|
*
|
||||||
* Test with: npx tsx src/cli-new.ts [args...]
|
* Test with: npx tsx src/cli-new.ts [args...]
|
||||||
*/
|
*/
|
||||||
process.title = "pi";
|
process.title = "sf";
|
||||||
|
|
||||||
import { setBedrockProviderModule } from "@singularity-forge/ai";
|
import { setBedrockProviderModule } from "@singularity-forge/ai";
|
||||||
import { bedrockProviderModule } from "@singularity-forge/ai/bedrock-provider";
|
import { bedrockProviderModule } from "@singularity-forge/ai/bedrock-provider";
|
||||||
|
|
|
||||||
|
|
@ -806,10 +806,11 @@ async function loadExtensionModule(extensionPath: string) {
|
||||||
* Check whether a module path belongs to a non-extension library that should
|
* Check whether a module path belongs to a non-extension library that should
|
||||||
* be silently skipped rather than reported as an error.
|
* be silently skipped rather than reported as an error.
|
||||||
*
|
*
|
||||||
* A directory is a non-extension library when its package.json has a "pi"
|
* A directory is a non-extension library when its package.json has an "sf"
|
||||||
* manifest that declares no extensions (e.g. `"pi": {}`). This is the
|
* (or "pi" for backwards compat) manifest that declares no extensions
|
||||||
* opt-out convention used by shared libraries like cmux that live inside
|
* (e.g. `"sf": {}`). This is the opt-out convention used by shared libraries
|
||||||
* the extensions/ directory but are not extensions themselves.
|
* like cmux that live inside the extensions/ directory but are not extensions
|
||||||
|
* themselves.
|
||||||
*
|
*
|
||||||
* This serves as a defense-in-depth check: even if the upstream discovery
|
* This serves as a defense-in-depth check: even if the upstream discovery
|
||||||
* layers fail to filter out the library, the loader itself will not emit
|
* layers fail to filter out the library, the loader itself will not emit
|
||||||
|
|
@ -824,10 +825,12 @@ function isNonExtensionLibrary(resolvedPath: string): boolean {
|
||||||
if (fs.existsSync(packageJsonPath)) {
|
if (fs.existsSync(packageJsonPath)) {
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
||||||
const pkg = JSON.parse(content);
|
const pkg = JSON.parse(content) as { sf?: SFManifest; pi?: SFManifest };
|
||||||
if (pkg.pi && typeof pkg.pi === "object") {
|
// Check sf field first, fall back to pi for backwards compat
|
||||||
// Has a pi manifest — check if it declares any extensions
|
const manifest = pkg.sf ?? pkg.pi;
|
||||||
const extensions = pkg.pi.extensions;
|
if (manifest && typeof manifest === "object") {
|
||||||
|
// Has an sf/pi manifest — check if it declares any extensions
|
||||||
|
const extensions = manifest.extensions;
|
||||||
if (!Array.isArray(extensions) || extensions.length === 0) {
|
if (!Array.isArray(extensions) || extensions.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -996,21 +999,19 @@ export async function loadExtensions(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PiManifest {
|
interface SFManifest {
|
||||||
extensions?: string[];
|
extensions?: string[];
|
||||||
themes?: string[];
|
themes?: string[];
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
prompts?: string[];
|
prompts?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPiManifest(packageJsonPath: string): PiManifest | null {
|
function readSFManifest(packageJsonPath: string): SFManifest | null {
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
const content = fs.readFileSync(packageJsonPath, "utf-8");
|
||||||
const pkg = JSON.parse(content);
|
const pkg = JSON.parse(content) as { sf?: SFManifest; pi?: SFManifest };
|
||||||
if (pkg.pi && typeof pkg.pi === "object") {
|
// Read sf field first, fall back to pi for backwards compat
|
||||||
return pkg.pi as PiManifest;
|
return pkg.sf ?? pkg.pi ?? null;
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -1027,20 +1028,20 @@ function isExtensionFile(name: string): boolean {
|
||||||
* Resolve extension entry points from a directory.
|
* Resolve extension entry points from a directory.
|
||||||
*
|
*
|
||||||
* Checks for:
|
* Checks for:
|
||||||
* 1. package.json with "pi.extensions" field -> returns declared paths
|
* 1. package.json with "sf.extensions" (or "pi.extensions") field -> returns declared paths
|
||||||
* 2. index.ts or index.js -> returns the index file
|
* 2. index.ts or index.js -> returns the index file
|
||||||
*
|
*
|
||||||
* Returns resolved paths or null if no entry points found.
|
* Returns resolved paths or null if no entry points found.
|
||||||
*/
|
*/
|
||||||
function resolveExtensionEntries(dir: string): string[] | null {
|
function resolveExtensionEntries(dir: string): string[] | null {
|
||||||
// Check for package.json with "pi" field first
|
// Check for package.json with "sf" or "pi" field first
|
||||||
const packageJsonPath = path.join(dir, "package.json");
|
const packageJsonPath = path.join(dir, "package.json");
|
||||||
if (fs.existsSync(packageJsonPath)) {
|
if (fs.existsSync(packageJsonPath)) {
|
||||||
const manifest = readPiManifest(packageJsonPath);
|
const manifest = readSFManifest(packageJsonPath);
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
// When a pi manifest exists, it is authoritative — don't fall through
|
// When an sf manifest exists, it is authoritative — don't fall through
|
||||||
// to index.ts/index.js auto-detection. This allows library directories
|
// to index.ts/index.js auto-detection. This allows library directories
|
||||||
// (like cmux) to opt out by declaring "pi": {} with no extensions.
|
// (like cmux) to opt out by declaring "sf": {} with no extensions.
|
||||||
if (!manifest.extensions?.length) {
|
if (!manifest.extensions?.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -1074,7 +1075,7 @@ function resolveExtensionEntries(dir: string): string[] | null {
|
||||||
* Discovery rules:
|
* Discovery rules:
|
||||||
* 1. Direct files: `extensions/*.ts` or `*.js` → load
|
* 1. Direct files: `extensions/*.ts` or `*.js` → load
|
||||||
* 2. Subdirectory with index: `extensions/* /index.ts` or `index.js` → load
|
* 2. Subdirectory with index: `extensions/* /index.ts` or `index.js` → load
|
||||||
* 3. Subdirectory with package.json: `extensions/* /package.json` with "pi" field → load what it declares
|
* 3. Subdirectory with package.json: `extensions/* /package.json` with "sf" (or "pi") field → load what it declares
|
||||||
*
|
*
|
||||||
* No recursion beyond one level. Complex packages must use package.json manifest.
|
* No recursion beyond one level. Complex packages must use package.json manifest.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1142,7 +1143,7 @@ export async function discoverAndLoadExtensions(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Project-local extensions: cwd/.pi/extensions/
|
// 1. Project-local extensions: cwd/.pi/extensions/ (also checks .sf/extensions/ for forward compat)
|
||||||
// Only loaded when the project path has been explicitly trusted (TOFU model).
|
// Only loaded when the project path has been explicitly trusted (TOFU model).
|
||||||
const localExtDir = path.join(cwd, ".pi", "extensions");
|
const localExtDir = path.join(cwd, ".pi", "extensions");
|
||||||
const localDiscovered = discoverExtensionsInDir(localExtDir);
|
const localDiscovered = discoverExtensionsInDir(localExtDir);
|
||||||
|
|
@ -1154,7 +1155,7 @@ export async function discoverAndLoadExtensions(
|
||||||
);
|
);
|
||||||
if (untrusted.length > 0) {
|
if (untrusted.length > 0) {
|
||||||
process.stderr.write(
|
process.stderr.write(
|
||||||
`[pi] Skipping ${untrusted.length} project-local extension(s) in ${localExtDir} — project not trusted. Use trustProject() to enable.\n`,
|
`[sf] Skipping ${untrusted.length} project-local extension(s) in ${localExtDir} — project not trusted. Use trustProject() to enable.\n`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const trusted = localDiscovered.filter((p) => !untrusted.includes(p));
|
const trusted = localDiscovered.filter((p) => !untrusted.includes(p));
|
||||||
|
|
|
||||||
|
|
@ -179,13 +179,13 @@ describe("collectRuntimeDependencies", () => {
|
||||||
describe("verifyRuntimeDependencies", () => {
|
describe("verifyRuntimeDependencies", () => {
|
||||||
it("does not throw for empty deps array", () => {
|
it("does not throw for empty deps array", () => {
|
||||||
assert.doesNotThrow(() =>
|
assert.doesNotThrow(() =>
|
||||||
verifyRuntimeDependencies([], "test-source", "pi"),
|
verifyRuntimeDependencies([], "test-source", "sf"),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not throw when all deps are present", () => {
|
it("does not throw when all deps are present", () => {
|
||||||
assert.doesNotThrow(() =>
|
assert.doesNotThrow(() =>
|
||||||
verifyRuntimeDependencies(["node"], "test-source", "pi"),
|
verifyRuntimeDependencies(["node"], "test-source", "sf"),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ describe("verifyRuntimeDependencies", () => {
|
||||||
verifyRuntimeDependencies(
|
verifyRuntimeDependencies(
|
||||||
["__nonexistent_dep_for_test__"],
|
["__nonexistent_dep_for_test__"],
|
||||||
"test-source",
|
"test-source",
|
||||||
"pi",
|
"sf",
|
||||||
),
|
),
|
||||||
(err: Error) => {
|
(err: Error) => {
|
||||||
assert.ok(err.message.includes("Missing runtime dependencies"));
|
assert.ok(err.message.includes("Missing runtime dependencies"));
|
||||||
|
|
@ -211,7 +211,7 @@ describe("verifyRuntimeDependencies", () => {
|
||||||
verifyRuntimeDependencies(
|
verifyRuntimeDependencies(
|
||||||
["__missing_1__", "__missing_2__"],
|
["__missing_1__", "__missing_2__"],
|
||||||
"test-source",
|
"test-source",
|
||||||
"pi",
|
"sf",
|
||||||
),
|
),
|
||||||
(err: Error) => {
|
(err: Error) => {
|
||||||
assert.ok(err.message.includes("__missing_1__"));
|
assert.ok(err.message.includes("__missing_1__"));
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ describe("runPackageCommand lifecycle hooks", () => {
|
||||||
"package.json": JSON.stringify({
|
"package.json": JSON.stringify({
|
||||||
name: "ext-registered",
|
name: "ext-registered",
|
||||||
type: "module",
|
type: "module",
|
||||||
pi: { extensions: ["./index.js"] },
|
sf: { extensions: ["./index.js"] },
|
||||||
}),
|
}),
|
||||||
"index.js": [
|
"index.js": [
|
||||||
'import { writeFileSync } from "node:fs";',
|
'import { writeFileSync } from "node:fs";',
|
||||||
|
|
@ -101,7 +101,7 @@ describe("runPackageCommand lifecycle hooks", () => {
|
||||||
"package.json": JSON.stringify({
|
"package.json": JSON.stringify({
|
||||||
name: "ext-legacy",
|
name: "ext-legacy",
|
||||||
type: "module",
|
type: "module",
|
||||||
pi: { extensions: ["./index.js"] },
|
sf: { extensions: ["./index.js"] },
|
||||||
}),
|
}),
|
||||||
"index.js": [
|
"index.js": [
|
||||||
'import { writeFileSync } from "node:fs";',
|
'import { writeFileSync } from "node:fs";',
|
||||||
|
|
@ -175,7 +175,7 @@ describe("runPackageCommand lifecycle hooks", () => {
|
||||||
"package.json": JSON.stringify({
|
"package.json": JSON.stringify({
|
||||||
name: "ext-empty",
|
name: "ext-empty",
|
||||||
type: "module",
|
type: "module",
|
||||||
pi: { extensions: ["./index.js"] },
|
sf: { extensions: ["./index.js"] },
|
||||||
}),
|
}),
|
||||||
"index.js": "export default function () {}",
|
"index.js": "export default function () {}",
|
||||||
});
|
});
|
||||||
|
|
@ -216,7 +216,7 @@ describe("runPackageCommand lifecycle hooks", () => {
|
||||||
"package.json": JSON.stringify({
|
"package.json": JSON.stringify({
|
||||||
name: "ext-runtime-deps",
|
name: "ext-runtime-deps",
|
||||||
type: "module",
|
type: "module",
|
||||||
pi: { extensions: ["./index.js"] },
|
sf: { extensions: ["./index.js"] },
|
||||||
}),
|
}),
|
||||||
"index.js": "export default function () {}",
|
"index.js": "export default function () {}",
|
||||||
"extension-manifest.json": JSON.stringify({
|
"extension-manifest.json": JSON.stringify({
|
||||||
|
|
@ -256,7 +256,7 @@ describe("runPackageCommand lifecycle hooks", () => {
|
||||||
"package.json": JSON.stringify({
|
"package.json": JSON.stringify({
|
||||||
name: "ext-after-remove",
|
name: "ext-after-remove",
|
||||||
type: "module",
|
type: "module",
|
||||||
pi: { extensions: ["./index.js"] },
|
sf: { extensions: ["./index.js"] },
|
||||||
}),
|
}),
|
||||||
"index.js": [
|
"index.js": [
|
||||||
'import { writeFileSync, existsSync } from "node:fs";',
|
'import { writeFileSync, existsSync } from "node:fs";',
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ type LocalSource = {
|
||||||
|
|
||||||
type ParsedSource = NpmSource | GitSource | LocalSource;
|
type ParsedSource = NpmSource | GitSource | LocalSource;
|
||||||
|
|
||||||
interface PiManifest {
|
interface SFManifest {
|
||||||
extensions?: string[];
|
extensions?: string[];
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
prompts?: string[];
|
prompts?: string[];
|
||||||
|
|
@ -443,11 +443,11 @@ function collectAutoThemeEntries(dir: string): string[] {
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPiManifestFile(packageJsonPath: string): PiManifest | null {
|
function readSFManifestFile(packageJsonPath: string): SFManifest | null {
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(packageJsonPath, "utf-8");
|
const content = readFileSync(packageJsonPath, "utf-8");
|
||||||
const pkg = JSON.parse(content) as { pi?: PiManifest };
|
const pkg = JSON.parse(content) as { sf?: SFManifest; pi?: SFManifest };
|
||||||
return pkg.pi ?? null;
|
return pkg.sf ?? pkg.pi ?? null;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -456,11 +456,11 @@ function readPiManifestFile(packageJsonPath: string): PiManifest | null {
|
||||||
function resolveExtensionEntries(dir: string): string[] | null {
|
function resolveExtensionEntries(dir: string): string[] | null {
|
||||||
const packageJsonPath = join(dir, "package.json");
|
const packageJsonPath = join(dir, "package.json");
|
||||||
if (existsSync(packageJsonPath)) {
|
if (existsSync(packageJsonPath)) {
|
||||||
const manifest = readPiManifestFile(packageJsonPath);
|
const manifest = readSFManifestFile(packageJsonPath);
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
// When a pi manifest exists, it is authoritative — don't fall through
|
// When an sf/pi manifest exists, it is authoritative — don't fall through
|
||||||
// to index.ts/index.js auto-detection. This allows library directories
|
// to index.ts/index.js auto-detection. This allows library directories
|
||||||
// (like cmux) to opt out by declaring "pi": {} with no extensions.
|
// (like cmux) to opt out by declaring "sf": {} (or "pi": {}) with no extensions.
|
||||||
if (!manifest.extensions?.length) {
|
if (!manifest.extensions?.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -1728,10 +1728,10 @@ export class DefaultPackageManager implements PackageManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const manifest = this.readPiManifest(packageRoot);
|
const manifest = this.readSFManifest(packageRoot);
|
||||||
if (manifest) {
|
if (manifest) {
|
||||||
for (const resourceType of RESOURCE_TYPES) {
|
for (const resourceType of RESOURCE_TYPES) {
|
||||||
const entries = manifest[resourceType as keyof PiManifest];
|
const entries = manifest[resourceType as keyof SFManifest];
|
||||||
this.addManifestEntries(
|
this.addManifestEntries(
|
||||||
entries,
|
entries,
|
||||||
packageRoot,
|
packageRoot,
|
||||||
|
|
@ -1769,8 +1769,8 @@ export class DefaultPackageManager implements PackageManager {
|
||||||
target: Map<string, { metadata: PathMetadata; enabled: boolean }>,
|
target: Map<string, { metadata: PathMetadata; enabled: boolean }>,
|
||||||
metadata: PathMetadata,
|
metadata: PathMetadata,
|
||||||
): void {
|
): void {
|
||||||
const manifest = this.readPiManifest(packageRoot);
|
const manifest = this.readSFManifest(packageRoot);
|
||||||
const entries = manifest?.[resourceType as keyof PiManifest];
|
const entries = manifest?.[resourceType as keyof SFManifest];
|
||||||
if (entries) {
|
if (entries) {
|
||||||
this.addManifestEntries(
|
this.addManifestEntries(
|
||||||
entries,
|
entries,
|
||||||
|
|
@ -1826,8 +1826,8 @@ export class DefaultPackageManager implements PackageManager {
|
||||||
packageRoot: string,
|
packageRoot: string,
|
||||||
resourceType: ResourceType,
|
resourceType: ResourceType,
|
||||||
): { allFiles: string[]; enabledByManifest: Set<string> } {
|
): { allFiles: string[]; enabledByManifest: Set<string> } {
|
||||||
const manifest = this.readPiManifest(packageRoot);
|
const manifest = this.readSFManifest(packageRoot);
|
||||||
const entries = manifest?.[resourceType as keyof PiManifest];
|
const entries = manifest?.[resourceType as keyof SFManifest];
|
||||||
if (entries && entries.length > 0) {
|
if (entries && entries.length > 0) {
|
||||||
const allFiles = this.collectFilesFromManifestEntries(
|
const allFiles = this.collectFilesFromManifestEntries(
|
||||||
entries,
|
entries,
|
||||||
|
|
@ -1850,7 +1850,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||||
return { allFiles, enabledByManifest: new Set(allFiles) };
|
return { allFiles, enabledByManifest: new Set(allFiles) };
|
||||||
}
|
}
|
||||||
|
|
||||||
private readPiManifest(packageRoot: string): PiManifest | null {
|
private readSFManifest(packageRoot: string): SFManifest | null {
|
||||||
const packageJsonPath = join(packageRoot, "package.json");
|
const packageJsonPath = join(packageRoot, "package.json");
|
||||||
if (!existsSync(packageJsonPath)) {
|
if (!existsSync(packageJsonPath)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1858,8 +1858,8 @@ export class DefaultPackageManager implements PackageManager {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(packageJsonPath, "utf-8");
|
const content = readFileSync(packageJsonPath, "utf-8");
|
||||||
const pkg = JSON.parse(content) as { pi?: PiManifest };
|
const pkg = JSON.parse(content) as { sf?: SFManifest; pi?: SFManifest };
|
||||||
return pkg.pi ?? null;
|
return pkg.sf ?? pkg.pi ?? null;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ function getGroupLabel(metadata: PathMetadata): string {
|
||||||
}
|
}
|
||||||
// Top-level resources
|
// Top-level resources
|
||||||
if (metadata.source === "auto") {
|
if (metadata.source === "auto") {
|
||||||
return metadata.scope === "user" ? "User (~/.pi/agent/)" : "Project (.pi/)";
|
return metadata.scope === "user" ? "User (~/.sf/agent/)" : "Project (.sf/)";
|
||||||
}
|
}
|
||||||
return metadata.scope === "user" ? "User settings" : "Project settings";
|
return metadata.scope === "user" ? "User settings" : "Project settings";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3206,7 +3206,7 @@ export class InteractiveMode {
|
||||||
|
|
||||||
const currentText =
|
const currentText =
|
||||||
this.editor.getExpandedText?.() ?? this.editor.getText();
|
this.editor.getExpandedText?.() ?? this.editor.getText();
|
||||||
const tmpFile = path.join(os.tmpdir(), `pi-editor-${Date.now()}.pi.md`);
|
const tmpFile = path.join(os.tmpdir(), `sf-editor-${Date.now()}.sf.md`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Write current content to temp file
|
// Write current content to temp file
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export function isFocusable(
|
||||||
* Components emit this at the cursor position when focused.
|
* Components emit this at the cursor position when focused.
|
||||||
* TUI finds and strips this marker, then positions the hardware cursor there.
|
* TUI finds and strips this marker, then positions the hardware cursor there.
|
||||||
*/
|
*/
|
||||||
export const CURSOR_MARKER = "\x1b_pi:c\x07";
|
export const CURSOR_MARKER = "\x1b_sf:c\x07";
|
||||||
|
|
||||||
export { visibleWidth };
|
export { visibleWidth };
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue