Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions Docs/CLI_USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ pnpm logo

### `pnpm logo`

Prints the compact UTF-8 brain logo, using color only on interactive terminals that
support it. Use `pnpm logo --color=always` to force ANSI color or
Prints the unchanged compact UTF-8 brain followed by the original eight-line
SemantIQ block-character wordmark. Rose, blush, mauve and muted blue-gray color
is enabled only on interactive terminals that support it.
Use `pnpm logo --color=always` to force ANSI color or
`pnpm logo --color=never` for plain text. This is a workspace display command,
not a published TypeScript executable or a benchmark run.

Expand Down Expand Up @@ -81,11 +83,13 @@ semantiq info

### `logo`

Prints the bundled compact SemantIQ brain artwork without running an evaluation or
changing existing CLI output. Use `semantiq logo --color=auto|always|never`:
Prints the bundled compact brain and original SemantIQ block-character wordmark
without running an evaluation or changing other CLI output.
Use `semantiq logo --color=auto|always|never`:
`auto` colors interactive terminals and produces plain text for pipes,
`NO_COLOR`, or `TERM=dumb`. A UTF-8 monospaced terminal at least 65 columns
wide is recommended. README displays the corresponding colored SVG banner.
`NO_COLOR`, or `TERM=dumb`. A UTF-8 monospaced terminal at least 90 columns
wide is recommended. README displays the same source glyphs in an approved
blush/rose/mauve colored SVG banner. See `Docs/branding/COLOR_TOKENS.md`.
This is an opt-in display command, not benchmark qualification evidence.

### `evaluate`
Expand Down
37 changes: 37 additions & 0 deletions Docs/branding/COLOR_TOKENS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SemantIQ — Approved ASCII Branding Palette

**Status:** visual-design reference for the SemantIQ README and opt-in terminal logo. This is not a product version or scientific claim.

The user-approved reference uses **blush / warm peach / dusty rose / mauve / lavender / desaturated blue-gray**. Avoid the previous saturated cyan, teal, navy-on-white title styling.

## Fixed original glyph forms

- **Brain:** `packages/python/src/semantiq/assets/semantiq-logo.txt` — existing 27-row compact Unicode brain. The text glyphs and their positions are unchanged in this update.
- **Wordmark:** `packages/python/src/semantiq/assets/semantiq-wordmark.txt` — original eight-line `SemantIQ` block-character wordmark supplied by the project owner. Do not replace it with a typeset font.
- **README:** `Docs/branding/semantiq-brain-compact.svg` renders these exact glyph lines with monochrome Unicode characters and a color gradient. No raster text or image generation is used in the repo.
- **CLI:** combines the exact brain and wordmark files with one empty separator line. Color is only an ANSI presentation layer and never appears in piped text by default.

## Terminal ink palette

| Slot | RGB | Hex | Character |
| --- | --- | --- | --- |
| Warm blush | 255, 178, 174 | `#ffb2ae` | left |
| Rose pink | 252, 167, 185 | `#fca7b9` | |
| Dusty rose | 225, 166, 197 | `#e1a6c5` | |
| Mauve | 198, 170, 211 | `#c6aad3` | |
| Slate lavender | 160, 179, 204 | `#a0b3cc` | |
| Blue-gray highlight | 147, 176, 194 | `#93b0c2` | right |

These tones are selected to visually approximate the approved mockup/reference photo; they are **brand-design tokens**, not scientifically measured colors. SVG uses closely related gradient stops for legibility against its background.

## Browser and terminal surfaces

The SVG background blends muted blue-gray `#536a70`, dusty mauve `#a9828e`, warm rose `#d29a9f`, and slate `#56687c`, with gentle peach `#f9b6a7` bloom.

The CLI deliberately does not print a background color: it needs to remain legible under arbitrary terminal themes. `--color=auto` requires a TTY and honors `NO_COLOR` / `TERM=dumb`; `--color=always` is an explicit override.

## Integration and guardrails

Website design may reuse these colors in a future separate PR; this change does **not** edit website components or imply a web-theme release.

README and CLI always display the original SemantIQ ASCII title. No benchmark execution, evidence, qualification, agent operation, telemetry, provider request, scientific promotion, or release is tied to the logo.
562 changes: 66 additions & 496 deletions Docs/branding/semantiq-brain-compact.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# SemantIQ: Behavioral Evidence Infrastructure for AI Systems

<p align="center">
<img src="Docs/branding/semantiq-brain-compact.svg" alt="SemantIQ — compact violet, cyan and teal brain logo" width="640">
<img src="Docs/branding/semantiq-brain-compact.svg" alt="SemantIQ — original block-character wordmark beneath compact brain art in blush, mauve, and muted blue-gray" width="640">
</p>

*Compact logo derived from the updated Unicode brain art. The CLI uses a text-only variant.*
*The original SemantIQ block-character wordmark and compact Unicode brain, presented in the approved blush/rose/mauve palette. CLI outputs the same text glyphs with optional ANSI color.*

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Public Alpha: 0.1.0-alpha.2](https://img.shields.io/badge/Public%20Alpha-0.1.0--alpha.2-orange.svg)](CHANGELOG.md)
Expand Down Expand Up @@ -243,7 +243,7 @@ semantiq validate-language "DP-008 is associated with reduced FP-002 drift."
semantiq info
```

For the source-checkout workspace, `pnpm logo` prints the same compact logo without invoking a benchmark. Both commands accept `--color=auto|always|never`; piped output is plain text by default.
For the source-checkout workspace, `pnpm logo` prints the same compact brain **and original ASCII SemantIQ title** without invoking a benchmark. Both commands accept `--color=auto|always|never`; piped output is plain text by default. Use a UTF-8 monospaced terminal at least **90 columns** wide.

The Python CLI also provides `evaluate` (using synthetic/mock fixtures) and
`verify` (digest-format validation only). It does not expose `doctor`, `claims`,
Expand Down
8 changes: 8 additions & 0 deletions packages/python/src/semantiq/assets/semantiq-wordmark.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
█████████ █████ █████ ██████
███░░░░░███ ░░███ ░░███ ███░░░░███
░███ ░░░ ██████ █████████████ ██████ ████████ ███████ ░███ ███ ░░███
░░█████████ ███░░███░░███░░███░░███ ░░░░░███ ░░███░░███ ░░░███░ ░███ ░███ ░███
░░░░░░░░███░███████ ░███ ░███ ░███ ███████ ░███ ░███ ░███ ░███ ░███ ██░███
███ ░███░███░░░ ░███ ░███ ░███ ███░░███ ░███ ░███ ░███ ███ ░███ ░░███ ░░████
░░█████████ ░░██████ █████░███ █████░░████████ ████ █████ ░░█████ █████ ░░░██████░██
░░░░░░░░░ ░░░░░░ ░░░░░ ░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░
39 changes: 25 additions & 14 deletions packages/python/src/semantiq/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,40 @@ def main(args: Optional[List[str]] = None) -> int:
parsed = parser.parse_args(args)

if parsed.command == "logo":
logo = files("semantiq").joinpath("assets/semantiq-logo.txt").read_text(encoding="utf-8")
assets = files("semantiq").joinpath("assets")
brain = assets.joinpath("semantiq-logo.txt").read_text(encoding="utf-8")
wordmark = assets.joinpath("semantiq-wordmark.txt").read_text(encoding="utf-8")
logo = brain.rstrip("\n") + "\n\n" + wordmark.rstrip("\n") + "\n"
use_color = parsed.color == "always" or (
parsed.color == "auto"
and sys.stdout.isatty()
and "NO_COLOR" not in os.environ
and os.environ.get("TERM") != "dumb"
)
if use_color:
# Shared left-to-right blush, dusty rose, mauve and muted slate palette.
# Each ANSI span is display-only: stripping controls restores exact ASCII.
palette = (
"167;139;250",
"129;140;248",
"56;189;248",
"34;211;238",
"45;212;191",
"52;211;153",
)
lines = logo.splitlines(keepends=True)
count = len(lines)
logo = "".join(
f"\x1b[38;2;{palette[min(i * len(palette) // count, len(palette) - 1)]}m"
f"{line.removesuffix(chr(10))}\x1b[0m\n"
for i, line in enumerate(lines)
"255;178;174",
"252;167;185",
"225;166;197",
"198;170;211",
"160;179;204",
"147;176;194",
)
lines = logo.splitlines()
column_count = max(len(line) for line in lines)
colored = []
for line in lines:
spans = []
for i, tone in enumerate(palette):
start = i * column_count // len(palette)
end = (i + 1) * column_count // len(palette)
segment = line[start:end]
if segment:
spans.append(f"\x1b[38;2;{tone}m{segment}\x1b[0m")
colored.append("".join(spans))
logo = "\n".join(colored) + "\n"
print(logo, end="")
return 0

Expand Down
26 changes: 15 additions & 11 deletions packages/python/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,33 @@ def test_cli_verify_hash(capsys):
assert exit_code_bad == 1


def _expected_ascii_logo():
assets = Path(__file__).resolve().parents[1] / "src" / "semantiq" / "assets"
brain = (assets / "semantiq-logo.txt").read_text(encoding="utf-8")
title = (assets / "semantiq-wordmark.txt").read_text(encoding="utf-8")
assert len(brain.splitlines()) == 27
assert len(title.splitlines()) == 8
assert title.startswith(" █████████")
return brain.rstrip("\n") + "\n\n" + title.rstrip("\n") + "\n"


def test_cli_logo_matches_packaged_asset(capsys):
exit_code = main(["logo"])
assert exit_code == 0
assert main(["logo"]) == 0
captured = capsys.readouterr()
expected = (
Path(__file__).resolve().parents[1]
/ "src" / "semantiq" / "assets" / "semantiq-logo.txt"
).read_text(encoding="utf-8")
expected = _expected_ascii_logo()
assert captured.out == expected
assert "█████████" in captured.out
assert "░░" in captured.out
assert "\x1b[" not in captured.out


def test_cli_logo_force_color_and_plain_modes(capsys):
expected = (
Path(__file__).resolve().parents[1]
/ "src" / "semantiq" / "assets" / "semantiq-logo.txt"
).read_text(encoding="utf-8")
expected = _expected_ascii_logo()

assert main(["logo", "--color=always"]) == 0
colored = capsys.readouterr().out
assert "\x1b[38;2;" in colored
assert "\x1b[38;2;255;178;174m" in colored
assert "\x1b[38;2;147;176;194m" in colored
assert re.sub(r"\x1b\[[0-9;]*m", "", colored) == expected

assert main(["logo", "--color=never"]) == 0
Expand Down
40 changes: 22 additions & 18 deletions scripts/semantiq-logo.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/usr/bin/env node
import { readFileSync } from "node:fs";

const logo = readFileSync(
new URL("../packages/python/src/semantiq/assets/semantiq-logo.txt", import.meta.url),
"utf8"
);
const assets = new URL("../packages/python/src/semantiq/assets/", import.meta.url);
const brain = readFileSync(new URL("semantiq-logo.txt", assets), "utf8");
const wordmark = readFileSync(new URL("semantiq-wordmark.txt", assets), "utf8");
const logo = brain.trimEnd() + "\n\n" + wordmark.trimEnd() + "\n";
const options = process.argv.slice(2);

if (options.length > 1 || (options.length && !/^--color=(auto|always|never)$/.test(options[0]))) {
Expand All @@ -23,22 +23,26 @@ const color =
if (!color) {
process.stdout.write(logo);
} else {
// Shared blush / rose / mauve / slate ANSI palette.
const palette = [
"167;139;250",
"129;140;248",
"56;189;248",
"34;211;238",
"45;212;191",
"52;211;153"
"255;178;174",
"252;167;185",
"225;166;197",
"198;170;211",
"160;179;204",
"147;176;194"
];
const lines = logo.trimEnd().split("\n");
const output =
lines
.map((line, i) => {
const tone =
palette[Math.min(Math.floor((i * palette.length) / lines.length), palette.length - 1)];
return "\x1b[38;2;" + tone + "m" + line + "\x1b[0m";
const columnCount = Math.max(...lines.map((line) => line.length));
const colored = lines.map((line) =>
palette
.map((tone, i) => {
const start = Math.floor((i * columnCount) / palette.length);
const end = Math.floor(((i + 1) * columnCount) / palette.length);
const segment = line.slice(start, end);
return segment ? "\x1b[38;2;" + tone + "m" + segment + "\x1b[0m" : "";
})
.join("\n") + "\n";
process.stdout.write(output);
.join("")
);
process.stdout.write(colored.join("\n") + "\n");
}
38 changes: 28 additions & 10 deletions tests/unit/semantiq-logo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,50 @@ import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";

const root = fileURLToPath(new URL("../../", import.meta.url));
const art = readFileSync(
new URL("../../packages/python/src/semantiq/assets/semantiq-logo.txt", import.meta.url),
"utf8"
);
const assets = new URL("../../packages/python/src/semantiq/assets/", import.meta.url);
const brain = readFileSync(new URL("semantiq-logo.txt", assets), "utf8");
const wordmark = readFileSync(new URL("semantiq-wordmark.txt", assets), "utf8");
const art = brain.trimEnd() + "\n\n" + wordmark.trimEnd() + "\n";

const execute = (args: string[] = []) =>
execFileSync(process.execPath, ["scripts/semantiq-logo.mjs", ...args], {
cwd: root,
encoding: "utf8"
});

describe("SemantIQ compact terminal branding", () => {
it("links the colored README SVG rather than embedding oversized terminal art", () => {
describe("SemantIQ original ASCII branding and approved palette", () => {
it("preserves original wordmark and compact brain", () => {
expect(brain.trimEnd().split("\n")).toHaveLength(27);
expect(wordmark.trimEnd().split("\n")).toHaveLength(8);
expect(wordmark).toContain("░░█████████");
});

it("links the accessible approved README SVG with both ASCII shapes", () => {
const readme = readFileSync(new URL("../../README.md", import.meta.url), "utf8");
const banner = readFileSync(
new URL("../../Docs/branding/semantiq-brain-compact.svg", import.meta.url),
"utf8"
);
expect(readme).toContain('src="Docs/branding/semantiq-brain-compact.svg"');
expect(readme).not.toContain("█████████");
for (const line of brain.trimEnd().split("\n")) {
expect(banner).toContain(line);
}
for (const line of wordmark.trimEnd().split("\n")) {
expect(banner).toContain(line);
}
expect(banner).toContain("#ffb2ae");
expect(banner).toContain("#a5b7cf");
});

it("prints compact art without ANSI when piped", () => {
it("prints the exact Unicode glyphs in plain mode", () => {
expect(execute()).toBe(art);
expect(execute(["--color=never"])).toBe(art);
});

it("supports forced ANSI color while preserving original glyphs", () => {
it("preserves original glyphs after ANSI stripping", () => {
const colored = execute(["--color=always"]);
expect(colored).toContain("\x1b[38;2;");
expect(colored).toContain("\x1b[38;2;255;178;174m");
expect(colored).toContain("\x1b[38;2;147;176;194m");
expect(colored.replace(/\x1b\[[0-9;]*m/g, "")).toBe(art);
});
});
Loading