feat: publish Sandwich Hime tooling preview

Publish the exact sanitized Agent Skill and VS Code preview source tree with independent license boundaries, deterministic provenance manifests, and no private development history. Material design and implementation assistance was provided by OpenAI Codex.

Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
2026-08-12 20:33:51 -04:00
commit 6df87bc958
51 changed files with 7424 additions and 0 deletions
@@ -0,0 +1,11 @@
<?sando go
package views
func Card(title string)
?>
<article class="card">
<?# Hime-san comment ?>
<h2><?= title ?></h2>
<? if title != "" { ?>
<?~ Badge(title) ?>
<? } ?>
</article>
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: AGPL-3.0-only
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const textmate = require('vscode-textmate');
const oniguruma = require('vscode-oniguruma');
test('mixed HTML and every Hime-san delimiter has stable scopes', async () => {
const root = path.join(__dirname, '..', '..');
const wasm = fs.readFileSync(require.resolve('vscode-oniguruma/release/onig.wasm'));
await oniguruma.loadWASM(wasm.buffer.slice(wasm.byteOffset, wasm.byteOffset + wasm.byteLength));
const registry = new textmate.Registry({
onigLib: Promise.resolve({createOnigScanner: (sources) => new oniguruma.OnigScanner(sources), createOnigString: (value) => new oniguruma.OnigString(value)}),
loadGrammar: async (scopeName) => {
if (scopeName === 'text.html.sando') return textmate.parseRawGrammar(fs.readFileSync(path.join(root, 'syntaxes', 'sando.tmLanguage.json'), 'utf8'), 'sando.tmLanguage.json');
if (scopeName === 'source.go') return {scopeName: 'source.go', patterns: [{match: '\\b(?:package|func|if)\\b', name: 'keyword.control.go'}]};
if (scopeName === 'text.html.basic') return {scopeName: 'text.html.basic', patterns: [{match: '</?[A-Za-z][^>]*>', name: 'meta.tag.html'}]};
return null;
},
});
const grammar = await registry.loadGrammar('text.html.sando');
const lines = fs.readFileSync(path.join(__dirname, 'fixtures', 'mixed.sando'), 'utf8').trimEnd().split('\n');
const seen = new Set();
let stack = textmate.INITIAL;
for (const line of lines) {
const result = grammar.tokenizeLine(line, stack);
stack = result.ruleStack;
for (const token of result.tokens) for (const scope of token.scopes) seen.add(scope);
}
const snapshot = [...seen].filter((scope) => scope.includes('sando')).sort();
assert.deepEqual(snapshot, [
'comment.block.sando', 'component.composition.sando', 'expression.output.sando',
'meta.embedded.block.sando.component', 'meta.embedded.block.sando.expression',
'meta.embedded.block.sando.header', 'meta.embedded.block.sando.statement',
'punctuation.definition.comment.begin.sando', 'punctuation.definition.comment.end.sando',
'punctuation.section.embedded.begin.sando', 'punctuation.section.embedded.end.sando',
'support.constant.language.sando', 'text.html.sando',
]);
assert.ok(seen.has('meta.tag.html'), 'visible HTML was not delegated to the HTML grammar');
});
+29
View File
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: AGPL-3.0-only
'use strict';
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const {downloadAndUnzipVSCode, runTests} = require('@vscode/test-electron');
(async () => {
const extensionDevelopmentPath = path.resolve(__dirname, '..', '..');
const sourceWorkspace = path.join(extensionDevelopmentPath, 'test', 'workspace');
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'himesan-vscode-integration-'));
const workspace = path.join(temporary, 'project [safe]');
fs.cpSync(sourceWorkspace, workspace, {recursive: true});
try {
const vscodeExecutablePath = await downloadAndUnzipVSCode('1.96.4');
await runTests({
vscodeExecutablePath,
extensionDevelopmentPath,
extensionTestsPath: path.join(__dirname, 'suite', 'index.js'),
launchArgs: [workspace, '--disable-extensions', '--disable-gpu', '--skip-welcome', '--skip-release-notes'],
extensionTestsEnv: {HIMESAN_TEST_BINARY: process.env.HIMESAN_TEST_BINARY || ''},
});
} finally {
fs.rmSync(temporary, {recursive: true, force: true});
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: AGPL-3.0-only
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vscode = require('vscode');
async function run() {
const binary = process.env.HIMESAN_TEST_BINARY;
assert.ok(binary && path.isAbsolute(binary), 'HIMESAN_TEST_BINARY must be an absolute Beta 2 candidate path');
const folder = vscode.workspace.workspaceFolders?.[0];
assert.ok(folder, 'integration workspace missing');
await vscode.workspace.getConfiguration('sandwichHime', folder.uri).update('himesanPath', binary, vscode.ConfigurationTarget.WorkspaceFolder);
const extension = vscode.extensions.getExtension('gamertan.sandwich-hime');
assert.ok(extension, 'extension not discovered');
await extension.activate();
const homeUri = vscode.Uri.joinPath(folder.uri, 'views', 'home.sando');
const pillUri = vscode.Uri.joinPath(folder.uri, 'views', 'badge.sando');
const badgeUri = vscode.Uri.joinPath(folder.uri, 'cards', 'badge.sando');
const duplicateUri = vscode.Uri.joinPath(folder.uri, 'views', 'duplicate.sando');
const document = await vscode.workspace.openTextDocument(homeUri);
await vscode.window.showTextDocument(document);
await waitFor(() => vscode.languages.getDiagnostics(homeUri).length === 0, 5000);
const pillPosition = document.positionAt(document.getText().indexOf('Pill') + 2);
const pillCompletions = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', homeUri, pillPosition, '.');
assert.ok(pillCompletions.items.some((item) => item.label === 'Pill'), 'same-package component completion missing');
const pillDefinitions = await vscode.commands.executeCommand('vscode.executeDefinitionProvider', homeUri, pillPosition);
assert.ok(pillDefinitions.some((item) => item.uri.toString() === pillUri.toString()), 'same-package component definition missing');
const badgePosition = document.positionAt(document.getText().indexOf('cards.Badge') + 'cards.Ba'.length);
const badgeCompletions = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', homeUri, badgePosition, '.');
assert.ok(badgeCompletions.items.some((item) => item.label === 'cards.Badge'), 'imported component completion missing');
const badgeDefinitions = await vscode.commands.executeCommand('vscode.executeDefinitionProvider', homeUri, badgePosition);
assert.ok(badgeDefinitions.some((item) => item.uri.toString() === badgeUri.toString()), 'imported component definition missing');
const hovers = await vscode.commands.executeCommand('vscode.executeHoverProvider', homeUri, badgePosition);
assert.ok(hovers.length > 0, 'component hover missing');
const symbols = await vscode.commands.executeCommand('vscode.executeDocumentSymbolProvider', homeUri);
assert.ok(symbols.some((item) => item.name === 'Home'), 'document component symbol missing');
const duplicate = Buffer.from('<?sando go\npackage views\nfunc Home(name string)\n?>\n<p><?= name ?></p>\n');
await vscode.workspace.fs.writeFile(duplicateUri, duplicate);
const duplicateDocument = await vscode.workspace.openTextDocument(duplicateUri);
await vscode.window.showTextDocument(duplicateDocument);
await waitFor(() => vscode.languages.getDiagnostics(duplicateUri).some((item) => item.code === 'HIM1500'), 15000);
await waitFor(() => vscode.languages.getDiagnostics(homeUri).some((item) => item.code === 'HIM1500'), 15000);
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.workspace.fs.delete(duplicateUri);
await vscode.commands.executeCommand('sandwichHime.restartLanguageServer');
await waitFor(() => !vscode.languages.getDiagnostics(homeUri).some((item) => item.code === 'HIM1500'), 15000);
const editor = await vscode.window.showTextDocument(document);
const end = document.positionAt(document.getText().length);
await editor.edit((builder) => builder.insert(end, '\n<div>'));
await waitFor(() => vscode.languages.getDiagnostics(homeUri).some((item) => item.code === 'HIM1311'), 5000);
assert.equal(fs.existsSync(`${homeUri.fsPath}.go`), false, 'LSP wrote generated output');
const brokenStart = document.positionAt(document.getText().lastIndexOf('\n<div>'));
await editor.edit((builder) => builder.delete(new vscode.Range(brokenStart, document.positionAt(document.getText().length))));
await waitFor(() => !vscode.languages.getDiagnostics(homeUri).some((item) => item.code === 'HIM1311'), 5000);
await vscode.workspace.fs.delete(badgeUri);
await vscode.commands.executeCommand('sandwichHime.restartLanguageServer');
await waitFor(async () => {
const result = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', homeUri, badgePosition, '.');
return !result.items.some((item) => item.label === 'cards.Badge');
}, 5000);
}
async function waitFor(predicate, timeout) {
const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
if (await predicate()) return;
await new Promise((resolve) => setTimeout(resolve, 100));
}
throw new Error('timed out waiting for integration condition');
}
module.exports = {run};
+56
View File
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: AGPL-3.0-only
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const core = require('../../src/core');
test('requires Beta 2 feature and ABI', () => {
assert.equal(core.validateVersion({compiler: 'v1.0.0-beta.2', runtime_abi: 'sando.v1', features: ['lsp-stdio']}), '');
assert.match(core.validateVersion({compiler: 'v1.0.0-beta.1', runtime_abi: 'sando.v1', features: []}), /beta\.2/);
assert.match(core.validateVersion({compiler: 'v1.0.0-beta.2', runtime_abi: 'wrong', features: ['lsp-stdio']}), /ABI/);
assert.match(core.validateVersion({compiler: 'v1.0.0-beta.2', runtime_abi: 'sando.v1', features: []}), /lsp-stdio/);
});
test('requires configured executable to be absolute', () => {
assert.equal(core.resolveExecutable(''), 'himesan');
assert.throws(() => core.resolveExecutable('bin/himesan'), /absolute/);
const absolute = path.resolve('bin/himesan');
assert.equal(core.resolveExecutable(absolute), path.normalize(absolute));
});
test('selects active workspace and refuses ambiguous fallback', () => {
const folders = [{uri: 'file:///one', name: 'one'}, {uri: 'file:///two', name: 'two'}];
assert.equal(core.selectWorkspace(folders, 'file:///two/views/home.sando').name, 'two');
assert.equal(core.selectWorkspace(folders, ''), null);
assert.equal(core.selectWorkspace([folders[0]], '').name, 'one');
});
test('installation guidance is pinned and runtime-first', () => {
const guidance = core.installationGuidance();
assert.ok(guidance.indexOf('sando@v1.0.0-beta.1') < guidance.indexOf('himesan@v1.0.0-beta.2'));
assert.equal(guidance.includes('@latest'), false);
});
test('workspace trust is a hard executable boundary', () => {
assert.equal(core.canExecute(true), true);
assert.equal(core.canExecute(false), false);
assert.equal(core.canExecute(undefined), false);
});
test('workspace files cannot add executable roots outside their directory tree', () => {
const workspace = {scheme: 'file', fsPath: path.resolve('trusted', 'project.code-workspace')};
assert.equal(core.isTrustedWorkspaceFolder({fsPath: path.resolve('trusted', 'one')}, workspace), true);
assert.equal(core.isTrustedWorkspaceFolder({fsPath: path.resolve('elsewhere')}, workspace), false);
assert.equal(core.isTrustedWorkspaceFolder({fsPath: path.resolve('trusted', 'one')}, undefined), true);
});
test('process runner reports failure and cancellation without a shell', async () => {
const failure = await core.runProcess(process.execPath, ['-e', 'process.exit(7)'], {timeout: 5000});
assert.equal(failure.code, 7);
let cancel;
const token = {onCancellationRequested(callback) { cancel = callback; return {dispose() {}}; }};
const running = core.runProcess(process.execPath, ['-e', 'setInterval(() => {}, 1000)'], {timeout: 5000, token});
cancel();
await assert.rejects(running, /canceled/);
});
@@ -0,0 +1,5 @@
<?sando go
package cards
func Badge(label string)
?>
<strong><?= label ?></strong>
+3
View File
@@ -0,0 +1,3 @@
module example.test/editor
go 1.25
@@ -0,0 +1,5 @@
<?sando go
package views
func Pill(label string)
?>
<span><?= label ?></span>
@@ -0,0 +1,10 @@
<?sando go
package views
import "example.test/editor/cards"
func Home(name string)
?>
<main>
<h1><?= name ?></h1>
<?~ Pill("Beta 2") ?>
<?~ cards.Badge("Imported") ?>
</main>