diff --git a/veans/internal/commands/create.go b/veans/internal/commands/create.go
new file mode 100644
index 000000000..21e218b66
--- /dev/null
+++ b/veans/internal/commands/create.go
@@ -0,0 +1,134 @@
+package commands
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/spf13/cobra"
+
+ "code.vikunja.io/veans/internal/client"
+ "code.vikunja.io/veans/internal/output"
+ "code.vikunja.io/veans/internal/status"
+)
+
+type createFlags struct {
+ description string
+ statusName string
+ priority int64
+ labels []string
+ parent string
+ blockedBy []string
+}
+
+func newCreateCmd() *cobra.Command {
+ f := &createFlags{}
+ cmd := &cobra.Command{
+ Use: "create
",
+ Aliases: []string{"c"},
+ Short: "Create a new task",
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ rt, err := loadRuntime()
+ if err != nil {
+ return err
+ }
+ task, err := runCreate(cmd.Context(), rt, args[0], f)
+ if err != nil {
+ return err
+ }
+ if globals.JSON {
+ return json.NewEncoder(cmd.OutOrStdout()).Encode(task)
+ }
+ fmt.Fprintf(cmd.OutOrStdout(), "Created %s %s\n",
+ rt.cfg.FormatTaskID(task.Index), task.Title)
+ return nil
+ },
+ }
+ cmd.Flags().StringVarP(&f.description, "description", "d", "", "task description (markdown)")
+ cmd.Flags().StringVarP(&f.statusName, "status", "s", "todo", "initial status (defaults to todo)")
+ cmd.Flags().Int64Var(&f.priority, "priority", 0, "priority (0=unset, 1=low, 5=DO_NOW)")
+ cmd.Flags().StringSliceVar(&f.labels, "label", nil, "labels to attach (repeatable; veans: prefix added if missing)")
+ cmd.Flags().StringVar(&f.parent, "parent", "", "parent task ID (creates parenttask relation)")
+ cmd.Flags().StringSliceVar(&f.blockedBy, "blocked-by", nil, "task IDs that block this one (repeatable)")
+ return cmd
+}
+
+func runCreate(ctx context.Context, rt *runtime, title string, f *createFlags) (*client.Task, error) {
+ st, err := status.Parse(f.statusName)
+ if err != nil {
+ return nil, err
+ }
+ bucketID, err := status.BucketID(st, rt.cfg.Buckets)
+ if err != nil {
+ return nil, err
+ }
+
+ created, err := rt.client.CreateTask(ctx, rt.cfg.ProjectID, &client.Task{
+ Title: strings.TrimSpace(title),
+ Description: f.description,
+ Priority: f.priority,
+ ProjectID: rt.cfg.ProjectID,
+ BucketID: bucketID,
+ Done: st.Done(),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // If the initial bucket isn't where Vikunja put it (defaults to first
+ // bucket on the view), nudge it explicitly.
+ if created.BucketID != bucketID {
+ updated, err := rt.client.UpdateTask(ctx, created.ID, &client.Task{
+ ID: created.ID,
+ BucketID: bucketID,
+ Done: st.Done(),
+ })
+ if err != nil {
+ return nil, output.Wrap(output.CodeUnknown, err, "set initial bucket: %v", err)
+ }
+ created = updated
+ }
+
+ // Attach labels (lazily creating them under veans: namespace).
+ for _, raw := range f.labels {
+ title := normalizeLabelTitle(raw)
+ l, err := getOrCreateLabelByTitle(ctx, rt.client, title)
+ if err != nil {
+ return nil, output.Wrap(output.CodeUnknown, err, "label %q: %v", title, err)
+ }
+ if err := rt.client.AddLabelToTask(ctx, created.ID, l.ID); err != nil {
+ return nil, err
+ }
+ }
+
+ // Parent relation.
+ if f.parent != "" {
+ parentID, err := rt.resolveTaskID(ctx, f.parent)
+ if err != nil {
+ return nil, err
+ }
+ if _, err := rt.client.CreateRelation(ctx, created.ID, parentID, "parenttask"); err != nil {
+ return nil, err
+ }
+ }
+
+ // Blocked-by relations.
+ for _, ref := range f.blockedBy {
+ blockerID, err := rt.resolveTaskID(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+ if _, err := rt.client.CreateRelation(ctx, created.ID, blockerID, "blocked"); err != nil {
+ return nil, err
+ }
+ }
+
+ // Re-fetch so the response reflects the labels and any post-create state.
+ final, err := rt.client.GetTask(ctx, created.ID)
+ if err != nil {
+ return created, nil // partial success — caller still got a usable task
+ }
+ return final, nil
+}
diff --git a/veans/internal/commands/root.go b/veans/internal/commands/root.go
index 0c35df120..ff615ef55 100644
--- a/veans/internal/commands/root.go
+++ b/veans/internal/commands/root.go
@@ -37,6 +37,7 @@ func Root(version string) *cobra.Command {
root.AddCommand(newInitCmd())
root.AddCommand(newListCmd())
root.AddCommand(newShowCmd())
+ root.AddCommand(newCreateCmd())
return root
}