From fe96878867fa5cd27a0fdf4260b2ed8201964e6e Mon Sep 17 00:00:00 2001 From: "Nikita Koselev, AI Intrapreneur" <6383738+nikitakoselev@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:39:54 +0300 Subject: [PATCH 1/4] Rename packages to pulsewriter_* and fix imports --- README.md | 12 ++++++------ .../{contentkit_api => pulsewriter_api}/main.py | 4 ++-- .../__init__.py | 0 packages/cli/pulsewriter_cli/__main__.py | 4 ++++ .../{contentkit_cli => pulsewriter_cli}/main.py | 4 ++-- packages/core/contentkit_core/__init__.py | 1 - packages/core/pulsewriter_core/__init__.py | 15 +++++++++++++++ .../templates/blog.md.j2 | 0 .../templates/devto.md.j2 | 0 .../templates/linkedin.md.j2 | 0 .../templates/x_thread.txt.j2 | 0 .../transform.py | 0 12 files changed, 29 insertions(+), 11 deletions(-) rename packages/api/{contentkit_api => pulsewriter_api}/main.py (92%) rename packages/cli/{contentkit_cli => pulsewriter_cli}/__init__.py (100%) create mode 100644 packages/cli/pulsewriter_cli/__main__.py rename packages/cli/{contentkit_cli => pulsewriter_cli}/main.py (85%) delete mode 100644 packages/core/contentkit_core/__init__.py create mode 100644 packages/core/pulsewriter_core/__init__.py rename packages/core/{contentkit_core => pulsewriter_core}/templates/blog.md.j2 (100%) rename packages/core/{contentkit_core => pulsewriter_core}/templates/devto.md.j2 (100%) rename packages/core/{contentkit_core => pulsewriter_core}/templates/linkedin.md.j2 (100%) rename packages/core/{contentkit_core => pulsewriter_core}/templates/x_thread.txt.j2 (100%) rename packages/core/{contentkit_core => pulsewriter_core}/transform.py (100%) diff --git a/README.md b/README.md index 0e23c21..bef4011 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate pip install -e .[dev] # Try CLI -python -m PulseWriter_cli --help -python -m PulseWriter_cli transform examples/input.md --platforms linkedin x devto --out-dir ./out +python -m pulsewriter_cli --help +python -m pulsewriter_cli transform examples/input.md --platforms linkedin x devto --out-dir ./out # Run API -uvicorn PulseWriter_api.main:app --reload +uvicorn pulsewriter_api.main:app --reload # POST /generate with JSON: {"topic":"Impact to Cashflow","platforms":["blog","linkedin","x"]} ``` @@ -26,9 +26,9 @@ uvicorn PulseWriter_api.main:app --reload ``` packages/ - core/PulseWriter_core/ # Open core: transforms, templates, validators - cli/PulseWriter_cli/ # Typer CLI (calls core) - api/PulseWriter_api/ # FastAPI (calls core) + core/pulsewriter_core/ # Open core: transforms, templates, validators + cli/pulsewriter_cli/ # Typer CLI (calls core) + api/pulsewriter_api/ # FastAPI (calls core) examples/ # Sample input & config recipes/n8n/ # (Optional) n8n workflow stubs ``` diff --git a/packages/api/contentkit_api/main.py b/packages/api/pulsewriter_api/main.py similarity index 92% rename from packages/api/contentkit_api/main.py rename to packages/api/pulsewriter_api/main.py index 1e01380..fb996f5 100644 --- a/packages/api/contentkit_api/main.py +++ b/packages/api/pulsewriter_api/main.py @@ -1,9 +1,9 @@ from fastapi import FastAPI, Body from pydantic import BaseModel from typing import List, Optional, Dict, Any -from contentkit_core import TransformConfig, generate, revise +from pulsewriter_core import TransformConfig, generate, revise -app = FastAPI(title="contentkit API", version="0.1.0") +app = FastAPI(title="PulseWriter API", version="0.1.0") class GenerateRequest(BaseModel): topic: Optional[str] = None diff --git a/packages/cli/contentkit_cli/__init__.py b/packages/cli/pulsewriter_cli/__init__.py similarity index 100% rename from packages/cli/contentkit_cli/__init__.py rename to packages/cli/pulsewriter_cli/__init__.py diff --git a/packages/cli/pulsewriter_cli/__main__.py b/packages/cli/pulsewriter_cli/__main__.py new file mode 100644 index 0000000..3e6e96f --- /dev/null +++ b/packages/cli/pulsewriter_cli/__main__.py @@ -0,0 +1,4 @@ +from .main import app + +if __name__ == "__main__": + app() diff --git a/packages/cli/contentkit_cli/main.py b/packages/cli/pulsewriter_cli/main.py similarity index 85% rename from packages/cli/contentkit_cli/main.py rename to packages/cli/pulsewriter_cli/main.py index 4644388..aa40390 100644 --- a/packages/cli/contentkit_cli/main.py +++ b/packages/cli/pulsewriter_cli/main.py @@ -1,9 +1,9 @@ import typer from pathlib import Path from typing import List -from contentkit_core import TransformConfig, generate, save_text, load_markdown +from pulsewriter_core import TransformConfig, generate, save_text, load_markdown -app = typer.Typer(help="contentkit CLI โ€” transform a single markdown into platform drafts.") +app = typer.Typer(help="PulseWriter CLI โ€” transform a single markdown into platform drafts.") @app.command() def transform( diff --git a/packages/core/contentkit_core/__init__.py b/packages/core/contentkit_core/__init__.py deleted file mode 100644 index afcfc67..0000000 --- a/packages/core/contentkit_core/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .transform import TransformConfig, generate, revise diff --git a/packages/core/pulsewriter_core/__init__.py b/packages/core/pulsewriter_core/__init__.py new file mode 100644 index 0000000..23592ba --- /dev/null +++ b/packages/core/pulsewriter_core/__init__.py @@ -0,0 +1,15 @@ +from .transform import ( + TransformConfig, + generate, + revise, + load_markdown, + save_text, +) + +__all__ = [ + "TransformConfig", + "generate", + "revise", + "load_markdown", + "save_text", +] diff --git a/packages/core/contentkit_core/templates/blog.md.j2 b/packages/core/pulsewriter_core/templates/blog.md.j2 similarity index 100% rename from packages/core/contentkit_core/templates/blog.md.j2 rename to packages/core/pulsewriter_core/templates/blog.md.j2 diff --git a/packages/core/contentkit_core/templates/devto.md.j2 b/packages/core/pulsewriter_core/templates/devto.md.j2 similarity index 100% rename from packages/core/contentkit_core/templates/devto.md.j2 rename to packages/core/pulsewriter_core/templates/devto.md.j2 diff --git a/packages/core/contentkit_core/templates/linkedin.md.j2 b/packages/core/pulsewriter_core/templates/linkedin.md.j2 similarity index 100% rename from packages/core/contentkit_core/templates/linkedin.md.j2 rename to packages/core/pulsewriter_core/templates/linkedin.md.j2 diff --git a/packages/core/contentkit_core/templates/x_thread.txt.j2 b/packages/core/pulsewriter_core/templates/x_thread.txt.j2 similarity index 100% rename from packages/core/contentkit_core/templates/x_thread.txt.j2 rename to packages/core/pulsewriter_core/templates/x_thread.txt.j2 diff --git a/packages/core/contentkit_core/transform.py b/packages/core/pulsewriter_core/transform.py similarity index 100% rename from packages/core/contentkit_core/transform.py rename to packages/core/pulsewriter_core/transform.py From f53adfd40f010e622019eb5f5413133a1577f02a Mon Sep 17 00:00:00 2001 From: "Nikita Koselev, AI Intrapreneur" <6383738+nikitakoselev@users.noreply.github.com> Date: Sun, 17 Aug 2025 15:46:03 +0300 Subject: [PATCH 2/4] Adopt src layout and expose CLI entry point --- README.md | 14 ++++++++------ {packages/connectors => connectors}/github_pr.py | 0 pyproject.toml | 11 +++++++++-- {packages/api => src}/pulsewriter_api/main.py | 0 {packages/cli => src}/pulsewriter_cli/__init__.py | 0 {packages/cli => src}/pulsewriter_cli/__main__.py | 0 {packages/cli => src}/pulsewriter_cli/main.py | 0 .../core => src}/pulsewriter_core/__init__.py | 0 .../pulsewriter_core/templates/blog.md.j2 | 0 .../pulsewriter_core/templates/devto.md.j2 | 0 .../pulsewriter_core/templates/linkedin.md.j2 | 0 .../pulsewriter_core/templates/x_thread.txt.j2 | 0 .../core => src}/pulsewriter_core/transform.py | 0 {packages/tests => tests}/test_sanity.py | 0 14 files changed, 17 insertions(+), 8 deletions(-) rename {packages/connectors => connectors}/github_pr.py (100%) rename {packages/api => src}/pulsewriter_api/main.py (100%) rename {packages/cli => src}/pulsewriter_cli/__init__.py (100%) rename {packages/cli => src}/pulsewriter_cli/__main__.py (100%) rename {packages/cli => src}/pulsewriter_cli/main.py (100%) rename {packages/core => src}/pulsewriter_core/__init__.py (100%) rename {packages/core => src}/pulsewriter_core/templates/blog.md.j2 (100%) rename {packages/core => src}/pulsewriter_core/templates/devto.md.j2 (100%) rename {packages/core => src}/pulsewriter_core/templates/linkedin.md.j2 (100%) rename {packages/core => src}/pulsewriter_core/templates/x_thread.txt.j2 (100%) rename {packages/core => src}/pulsewriter_core/transform.py (100%) rename {packages/tests => tests}/test_sanity.py (100%) diff --git a/README.md b/README.md index bef4011..f7cd0bb 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate pip install -e .[dev] # Try CLI -python -m pulsewriter_cli --help -python -m pulsewriter_cli transform examples/input.md --platforms linkedin x devto --out-dir ./out +pulsewriter --help +pulsewriter examples/input.md --platforms linkedin --platforms x --platforms devto --out-dir ./out # Run API uvicorn pulsewriter_api.main:app --reload @@ -25,12 +25,14 @@ uvicorn pulsewriter_api.main:app --reload ## Project layout ``` -packages/ - core/pulsewriter_core/ # Open core: transforms, templates, validators - cli/pulsewriter_cli/ # Typer CLI (calls core) - api/pulsewriter_api/ # FastAPI (calls core) +src/ + pulsewriter_core/ # Open core: transforms, templates, validators + pulsewriter_cli/ # Typer CLI (calls core) + pulsewriter_api/ # FastAPI (calls core) examples/ # Sample input & config recipes/n8n/ # (Optional) n8n workflow stubs +tests/ # Test suite +connectors/ # Optional helper modules ``` ## License diff --git a/packages/connectors/github_pr.py b/connectors/github_pr.py similarity index 100% rename from packages/connectors/github_pr.py rename to connectors/github_pr.py diff --git a/pyproject.toml b/pyproject.toml index 40ff0c5..5e0b82f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,5 +22,12 @@ dependencies = [ [project.optional-dependencies] dev = ["pytest>=8.2.0", "pytest-cov>=5.0.0"] -[tool.setuptools.packages.find] -where = ["packages"] +[project.scripts] +pulsewriter = "pulsewriter_cli.main:app" + +[tool.setuptools] +packages = ["pulsewriter_core", "pulsewriter_cli", "pulsewriter_api"] +package-dir = {"" = "src"} + +[tool.setuptools.package-data] +pulsewriter_core = ["templates/*.j2"] diff --git a/packages/api/pulsewriter_api/main.py b/src/pulsewriter_api/main.py similarity index 100% rename from packages/api/pulsewriter_api/main.py rename to src/pulsewriter_api/main.py diff --git a/packages/cli/pulsewriter_cli/__init__.py b/src/pulsewriter_cli/__init__.py similarity index 100% rename from packages/cli/pulsewriter_cli/__init__.py rename to src/pulsewriter_cli/__init__.py diff --git a/packages/cli/pulsewriter_cli/__main__.py b/src/pulsewriter_cli/__main__.py similarity index 100% rename from packages/cli/pulsewriter_cli/__main__.py rename to src/pulsewriter_cli/__main__.py diff --git a/packages/cli/pulsewriter_cli/main.py b/src/pulsewriter_cli/main.py similarity index 100% rename from packages/cli/pulsewriter_cli/main.py rename to src/pulsewriter_cli/main.py diff --git a/packages/core/pulsewriter_core/__init__.py b/src/pulsewriter_core/__init__.py similarity index 100% rename from packages/core/pulsewriter_core/__init__.py rename to src/pulsewriter_core/__init__.py diff --git a/packages/core/pulsewriter_core/templates/blog.md.j2 b/src/pulsewriter_core/templates/blog.md.j2 similarity index 100% rename from packages/core/pulsewriter_core/templates/blog.md.j2 rename to src/pulsewriter_core/templates/blog.md.j2 diff --git a/packages/core/pulsewriter_core/templates/devto.md.j2 b/src/pulsewriter_core/templates/devto.md.j2 similarity index 100% rename from packages/core/pulsewriter_core/templates/devto.md.j2 rename to src/pulsewriter_core/templates/devto.md.j2 diff --git a/packages/core/pulsewriter_core/templates/linkedin.md.j2 b/src/pulsewriter_core/templates/linkedin.md.j2 similarity index 100% rename from packages/core/pulsewriter_core/templates/linkedin.md.j2 rename to src/pulsewriter_core/templates/linkedin.md.j2 diff --git a/packages/core/pulsewriter_core/templates/x_thread.txt.j2 b/src/pulsewriter_core/templates/x_thread.txt.j2 similarity index 100% rename from packages/core/pulsewriter_core/templates/x_thread.txt.j2 rename to src/pulsewriter_core/templates/x_thread.txt.j2 diff --git a/packages/core/pulsewriter_core/transform.py b/src/pulsewriter_core/transform.py similarity index 100% rename from packages/core/pulsewriter_core/transform.py rename to src/pulsewriter_core/transform.py diff --git a/packages/tests/test_sanity.py b/tests/test_sanity.py similarity index 100% rename from packages/tests/test_sanity.py rename to tests/test_sanity.py From 075fce790e78b6d74e5b69151d437502ecd2b4a3 Mon Sep 17 00:00:00 2001 From: "Nikita Koselev, AI Intrapreneur" <6383738+nikitakoselev@users.noreply.github.com> Date: Sun, 17 Aug 2025 15:46:08 +0300 Subject: [PATCH 3/4] Document platform-specific setup and clean up naming --- COMMERCIAL_LICENSE.md | 2 +- FEATURES.md | 2 +- README.md | 52 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/COMMERCIAL_LICENSE.md b/COMMERCIAL_LICENSE.md index f94dfff..624a288 100644 --- a/COMMERCIAL_LICENSE.md +++ b/COMMERCIAL_LICENSE.md @@ -1,6 +1,6 @@ # Commercial License Terms (Summary) -For **production or commercial use** of `contentkit`, you must obtain a commercial license from the Licensor. +For **production or commercial use** of `PulseWriter`, you must obtain a commercial license from the Licensor. Contact: nikita.koselev@gmail.com Typical grant includes: diff --git a/FEATURES.md b/FEATURES.md index 09afaea..397e4c6 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -4,7 +4,7 @@ Status: ๐ŸŸข Ready ยท ๐ŸŸก In Progress ยท ๐Ÿ”ด Blocked ยท โœ… Done | ID | Feature | Status | Notes / Acceptance Criteria | Owner | |----|---------|--------|-----------------------------|-------| -| F-001 | CLI: transform markdown โ†’ blog/LinkedIn/X/Dev.to | โœ… | `contentkit_cli transform` writes files to /out | core | +| F-001 | CLI: transform markdown โ†’ blog/LinkedIn/X/Dev.to | โœ… | `pulsewriter` writes files to /out | core | | F-002 | API: `/generate` + `/revise` | โœ… | Accepts `topic` or `body_markdown`, returns drafts | api | | F-003 | Templates: tone/persona system | โœ… | Jinja templates parametrize `{tone, persona, word_target}` | core | | F-004 | GitHub PR helper (blog repo) | ๐ŸŸข | Function: create branch, commit file, open PR to `nikitakoselev/nikitakoselev.github.io` | connectors | diff --git a/README.md b/README.md index f7cd0bb..636e818 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,59 @@ This MVP intentionally keeps logic simple (template-based) so you can ship fast; ## Quick start +### 1. Create and activate a virtual environment + +**macOS / Linux (bash or zsh)** + +```bash +python -m venv .venv +source .venv/bin/activate +``` + +**Windows PowerShell** + +```powershell +python -m venv .venv +\.venv\Scripts\Activate.ps1 +``` + +**Windows CMD** + +```cmd +python -m venv .venv +\.venv\Scripts\activate.bat +``` + +### 2. Install dependencies + +**macOS / Linux** + ```bash -# Create & activate venv (example) -python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e .[dev] +``` -# Install +**Windows PowerShell** + +```powershell +pip install -e ".[dev]" +``` + +**Windows CMD** + +```cmd pip install -e .[dev] +``` -# Try CLI +### 3. Run the CLI + +```bash pulsewriter --help pulsewriter examples/input.md --platforms linkedin --platforms x --platforms devto --out-dir ./out +``` -# Run API +### 4. Run the API + +```bash uvicorn pulsewriter_api.main:app --reload # POST /generate with JSON: {"topic":"Impact to Cashflow","platforms":["blog","linkedin","x"]} ``` @@ -45,7 +86,6 @@ You may run, modify, and use non-production internally. **Production/commercial - v0.2: add optional LLM augment in core (behind a flag) - v0.3: connectors (Buffer/Notion/GitHub PR helper) - v0.4: n8n nodes + Telegram bot adapter -``` --- From 38a55d2b7bc11f6a85005fa64ebe93c970be6f68 Mon Sep 17 00:00:00 2001 From: "Nikita Koselev, AI Intrapreneur" <6383738+nikitakoselev@users.noreply.github.com> Date: Sun, 17 Aug 2025 15:52:12 +0300 Subject: [PATCH 4/4] Clarify setup commands --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 636e818..862625f 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,24 @@ This MVP intentionally keeps logic simple (template-based) so you can ship fast; **macOS / Linux (bash or zsh)** ```bash -python -m venv .venv -source .venv/bin/activate +python -m venv .venv # -m: run the venv module to create env in .venv +source .venv/bin/activate # activate the virtual environment ``` **Windows PowerShell** ```powershell -python -m venv .venv -\.venv\Scripts\Activate.ps1 +python -m venv .venv # -m: run the venv module; .venv is env folder +.\.venv\Scripts\Activate.ps1 # activate the virtual environment ``` **Windows CMD** ```cmd python -m venv .venv -\.venv\Scripts\activate.bat +REM -m: run the venv module; .venv is env folder +.\.venv\Scripts\activate.bat +REM Activate the virtual environment ``` ### 2. Install dependencies @@ -34,32 +36,38 @@ python -m venv .venv **macOS / Linux** ```bash -pip install -e .[dev] +pip install -e .[dev] # -e: editable install; .[dev]: include dev extras ``` **Windows PowerShell** ```powershell -pip install -e ".[dev]" +pip install -e ".[dev]" # -e: editable install; "[dev]": include dev extras ``` **Windows CMD** ```cmd pip install -e .[dev] +REM -e: editable install +REM .[dev]: include dev extras ``` ### 3. Run the CLI ```bash -pulsewriter --help -pulsewriter examples/input.md --platforms linkedin --platforms x --platforms devto --out-dir ./out +pulsewriter --help # show CLI usage and exit +pulsewriter examples/input.md \ # source markdown + --platforms linkedin \ # include LinkedIn draft + --platforms x \ # include X (Twitter) draft + --platforms devto \ # include Dev.to draft + --out-dir ./out # write output files to ./out ``` ### 4. Run the API ```bash -uvicorn pulsewriter_api.main:app --reload +uvicorn pulsewriter_api.main:app --reload # --reload: restart on code changes # POST /generate with JSON: {"topic":"Impact to Cashflow","platforms":["blog","linkedin","x"]} ```